commit
712beceb51
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
# Box Clock
|
||||||
|
|
||||||
|
Box Clock is a customizable clock app for Bangle.js 2 that features an interactive drag and drop interface and easy JSON configuration.
|
||||||
|
|
||||||
|
## Unique Features
|
||||||
|
|
||||||
|
__Drag & Drop:__
|
||||||
|
|
||||||
|
This intuitive feature allows you to reposition any element (box) on the clock face with ease. Tap on the box(s) you want to move and the border will show, drag into position and tap outside of the boxes to finish placing. **Note:** Roll the tip of your finger slowly on the screen for fine adjustments.
|
||||||
|
|
||||||
|
__Double Tap to Save:__
|
||||||
|
|
||||||
|
After you've found the perfect position for your boxes, you can save their positions with a quick double tap on the background. This makes it easy to ensure your custom layout is stored for future use. A save icon will appear momentarily to confirm that your configuration has been saved.
|
||||||
|
|
||||||
|
__JSON Configuration:__
|
||||||
|
|
||||||
|
Each box can be customized extensively via a simple JSON configuration. You can add a custom text string to your configuration with the "string" parameter and you can match system theme colors by using "fg", "bg", "fg2", "bg2", "fgH", or "bgH" for any of the color parameters.
|
||||||
|
|
||||||
|
## Config File Structure
|
||||||
|
|
||||||
|
Here's what an example configuration might look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"customBox": { //
|
||||||
|
"string": "Your text here",
|
||||||
|
"font": "CustomFont", // Custom fonts must be removed in setUI
|
||||||
|
"fontSize": 1,
|
||||||
|
"outline": 2,
|
||||||
|
"color": "#FF9900", // Use 6 or 3 digit hex color codes
|
||||||
|
"outlineColor": "bgH", // Or match system theme colors like this
|
||||||
|
"border": 65535, // Or use 16-bit RGB565 like this
|
||||||
|
"xPadding": 1,
|
||||||
|
"yPadding": -4,
|
||||||
|
"xOffset": 0,
|
||||||
|
"yOffset": 3,
|
||||||
|
"boxPos": { "x": 0.5, "y": 0.5 }
|
||||||
|
},
|
||||||
|
"bg": { // Can also be removed for no background
|
||||||
|
"img": "YourImageName.img"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
__Breakdown of Parameters:__
|
||||||
|
|
||||||
|
* **string:** The text string to be displayed inside the box.
|
||||||
|
|
||||||
|
* **font:** The font name given to g.setFont()
|
||||||
|
|
||||||
|
* **fontSize:** The size of the font.
|
||||||
|
|
||||||
|
* **outline:** The thickness of the outline around the text.
|
||||||
|
|
||||||
|
* **color:** The color of the text.
|
||||||
|
|
||||||
|
* **outlineColor:** The color of the text outline.
|
||||||
|
|
||||||
|
* **border:** The color of the box border when moving.
|
||||||
|
|
||||||
|
* **xPadding, yPadding:** Additional padding around the text inside the box.
|
||||||
|
|
||||||
|
* **xOffset, yOffset:** Offsets the text position within the box.
|
||||||
|
|
||||||
|
* **boxPos:** Initial position of the box on the screen. Values are fractions of the screen width (x) and height (y), so { "x": 0.5, "y": 0.5 } would be in the middle of the screen.
|
||||||
|
|
||||||
|
* **bg:** This specifies a custom background image, with the img property defining the name of the image file on the Bangle.js storage.
|
||||||
|
|
||||||
|
## Multiple Configurations
|
||||||
|
|
||||||
|
The app includes a settings menu that allows you to switch between different configurations. The selected configuration is stored in the default JSON file alongside the other configuration data using the selectedConfig property.
|
||||||
|
|
||||||
|
If the selectedConfig property is not present or is set to 0, the app will use the default configuration. To create additional configurations, create separate JSON files with the naming convention boxclk-N.json, where N is the configuration number. The settings menu will list all available configurations.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
This app was built and tested with Bangle.js 2.
|
||||||
|
|
||||||
|
## Feedback
|
||||||
|
|
||||||
|
If you have any issues or suggestions, please open an issue on this GitHub repository. Contributions to improve the application are also welcomed.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
[stweedo](https://github.com/stweedo)
|
||||||
|
|
@ -0,0 +1,371 @@
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 1. Module dependencies and initial configurations
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let storage = require("Storage");
|
||||||
|
let locale = require("locale");
|
||||||
|
let widgets = require("widget_utils");
|
||||||
|
let date = new Date();
|
||||||
|
let bgImage;
|
||||||
|
let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0;
|
||||||
|
let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json';
|
||||||
|
// Add a condition to check if the file exists, if it does not, default to 'boxclk.json'
|
||||||
|
if (!storage.read(fileName)) {
|
||||||
|
fileName = 'boxclk.json';
|
||||||
|
}
|
||||||
|
let boxesConfig = storage.readJSON(fileName, 1) || {};
|
||||||
|
let boxes = {};
|
||||||
|
let boxPos = {};
|
||||||
|
let isDragging = {};
|
||||||
|
let wasDragging = {};
|
||||||
|
let doubleTapTimer = null;
|
||||||
|
let g_setColor;
|
||||||
|
|
||||||
|
let saveIcon = require("heatshrink").decompress(atob("mEwwkEogA/AHdP/4AK+gWVDBQWNAAIuVGBAIB+UQdhMfGBAHBCxUAgIXHIwPyCxQwEJAgXB+MAl/zBwQGBn8ggQjBGAQXG+EA/4XI/8gBIQXTGAMPC6n/C6HzkREBC6YACC6QAFC57aHCYIXOOgLsEn4XPABIX/C6vykQAEl6/WgCQBC5imFAAT2BC5gCBI4oUCC5x0IC/4X/C4K8Bl4XJ+TCCC4wKBABkvC4tEEoMQCxcBB4IWEC4XyDBUBFwIXGJAIAOIwowDABoWGGB4uHDBwWJAH4AzA"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 2. Graphical and visual configurations
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let w = g.getWidth();
|
||||||
|
let h = g.getHeight();
|
||||||
|
let totalWidth, totalHeight;
|
||||||
|
let enableSuffix = true;
|
||||||
|
let drawTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 3. Touchscreen Handlers
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let touchHandler;
|
||||||
|
let dragHandler;
|
||||||
|
let movementDistance = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 4. Font loading function
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let loadCustomFont = function() {
|
||||||
|
Graphics.prototype.setFontBrunoAce = function() {
|
||||||
|
// Actual height 23 (24 - 2)
|
||||||
|
return this.setFontCustom(
|
||||||
|
E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))),
|
||||||
|
46,
|
||||||
|
atob("CBEdChgYGhgaGBsaCQ=="),
|
||||||
|
32|65536
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 5. Initial settings of boxes and their positions
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
for (let key in boxesConfig) {
|
||||||
|
if (key === 'bg' && boxesConfig[key].img) {
|
||||||
|
bgImage = storage.read(boxesConfig[key].img);
|
||||||
|
} else if (key !== 'selectedConfig') {
|
||||||
|
boxes[key] = Object.assign({}, boxesConfig[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let boxKeys = Object.keys(boxes);
|
||||||
|
|
||||||
|
boxKeys.forEach((key) => {
|
||||||
|
let boxConfig = boxes[key];
|
||||||
|
boxPos[key] = {
|
||||||
|
x: w * boxConfig.boxPos.x,
|
||||||
|
y: h * boxConfig.boxPos.y
|
||||||
|
};
|
||||||
|
isDragging[key] = false;
|
||||||
|
wasDragging[key] = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 6. Text and drawing functions
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Overwrite the setColor function to allow the
|
||||||
|
// use of (x) in g.theme.x as a string
|
||||||
|
// in your JSON config ("fg", "bg", "fg2", "bg2", "fgH", "bgH")
|
||||||
|
let modSetColor = function() {
|
||||||
|
// Save the original setColor function
|
||||||
|
g_setColor = g.setColor;
|
||||||
|
// Overwrite setColor with the new function
|
||||||
|
g.setColor = function(color) {
|
||||||
|
if (typeof color === "string" && color in g.theme) {
|
||||||
|
g_setColor.call(g, g.theme[color]);
|
||||||
|
} else {
|
||||||
|
g_setColor.call(g, color);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let restoreSetColor = function() {
|
||||||
|
// Restore the original setColor function
|
||||||
|
if (g_setColor) {
|
||||||
|
g.setColor = g_setColor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Overwrite the drawString function
|
||||||
|
let g_drawString = g.drawString;
|
||||||
|
g.drawString = function(box, str, x, y) {
|
||||||
|
outlineText(box, str, x, y);
|
||||||
|
g.setColor(box.color);
|
||||||
|
g_drawString.call(g, str, x, y);
|
||||||
|
};
|
||||||
|
|
||||||
|
let outlineText = function(box, str, x, y) {
|
||||||
|
let px = box.outline;
|
||||||
|
let dx = [-px, 0, px, -px, px, -px, 0, px];
|
||||||
|
let dy = [-px, -px, -px, 0, 0, px, px, px];
|
||||||
|
g.setColor(box.outlineColor);
|
||||||
|
for (let i = 0; i < dx.length; i++) {
|
||||||
|
g_drawString.call(g, str, x + dx[i], y + dy[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let calcBoxSize = function(boxItem) {
|
||||||
|
g.reset();
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
g.setFont(boxItem.font, boxItem.fontSize);
|
||||||
|
let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline;
|
||||||
|
let fontHeight = g.getFontHeight() + 2 * boxItem.outline;
|
||||||
|
totalWidth = strWidth + 2 * boxItem.xPadding;
|
||||||
|
totalHeight = fontHeight + 2 * boxItem.yPadding;
|
||||||
|
};
|
||||||
|
|
||||||
|
let calcBoxPos = function(boxKey) {
|
||||||
|
return {
|
||||||
|
x1: boxPos[boxKey].x - totalWidth / 2,
|
||||||
|
y1: boxPos[boxKey].y - totalHeight / 2,
|
||||||
|
x2: boxPos[boxKey].x + totalWidth / 2,
|
||||||
|
y2: boxPos[boxKey].y + totalHeight / 2
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let displaySaveIcon = function() {
|
||||||
|
draw(boxes);
|
||||||
|
g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24);
|
||||||
|
// Display save icon for 2 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
g.clearRect(w / 2 - 24, h / 2 - 24, w / 2 + 24, h / 2 + 24);
|
||||||
|
draw(boxes);
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 7. Date and time related functions
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let getDate = function() {
|
||||||
|
const date = new Date();
|
||||||
|
const dayOfMonth = date.getDate();
|
||||||
|
const month = locale.month(date, 1);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
let suffix;
|
||||||
|
if ([1, 21, 31].includes(dayOfMonth)) {
|
||||||
|
suffix = "st";
|
||||||
|
} else if ([2, 22].includes(dayOfMonth)) {
|
||||||
|
suffix = "nd";
|
||||||
|
} else if ([3, 23].includes(dayOfMonth)) {
|
||||||
|
suffix = "rd";
|
||||||
|
} else {
|
||||||
|
suffix = "th";
|
||||||
|
}
|
||||||
|
let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth;
|
||||||
|
return month + " " + dayOfMonthStr + ", " + year;
|
||||||
|
};
|
||||||
|
|
||||||
|
let getDayOfWeek = function(date) {
|
||||||
|
return locale.dow(date, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 8. Main draw function
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let draw = function(boxes) {
|
||||||
|
date = new Date();
|
||||||
|
g.clear();
|
||||||
|
if (bgImage) {
|
||||||
|
g.drawImage(bgImage, 0, 0);
|
||||||
|
}
|
||||||
|
if (boxes.time) {
|
||||||
|
boxes.time.string = locale.time(date, 1);
|
||||||
|
}
|
||||||
|
if (boxes.date) {
|
||||||
|
boxes.date.string = getDate();
|
||||||
|
}
|
||||||
|
if (boxes.dow) {
|
||||||
|
boxes.dow.string = getDayOfWeek(date);
|
||||||
|
}
|
||||||
|
if (boxes.batt) {
|
||||||
|
boxes.batt.string = E.getBattery() + "%";
|
||||||
|
}
|
||||||
|
boxKeys.forEach((boxKey) => {
|
||||||
|
let boxItem = boxes[boxKey];
|
||||||
|
calcBoxSize(boxItem);
|
||||||
|
const pos = calcBoxPos(boxKey);
|
||||||
|
if (isDragging[boxKey]) {
|
||||||
|
g.setColor(boxItem.border);
|
||||||
|
g.drawRect(pos.x1, pos.y1, pos.x2, pos.y2);
|
||||||
|
}
|
||||||
|
g.drawString(
|
||||||
|
boxItem,
|
||||||
|
boxItem.string,
|
||||||
|
boxPos[boxKey].x + boxItem.xOffset,
|
||||||
|
boxPos[boxKey].y + boxItem.yOffset
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (!Object.values(isDragging).some(Boolean)) {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 9. Helper function for touch event
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let touchInText = function(e, boxItem, boxKey) {
|
||||||
|
calcBoxSize(boxItem);
|
||||||
|
const pos = calcBoxPos(boxKey);
|
||||||
|
return e.x >= pos.x1 &&
|
||||||
|
e.x <= pos.x2 &&
|
||||||
|
e.y >= pos.y1 &&
|
||||||
|
e.y <= pos.y2;
|
||||||
|
};
|
||||||
|
|
||||||
|
let deselectAllBoxes = function() {
|
||||||
|
Object.keys(isDragging).forEach((boxKey) => {
|
||||||
|
isDragging[boxKey] = false;
|
||||||
|
});
|
||||||
|
restoreSetColor();
|
||||||
|
widgets.show();
|
||||||
|
widgets.swipeOn();
|
||||||
|
modSetColor();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 10. Setup function to configure event handlers
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
let setup = function() {
|
||||||
|
// ------------------------------------
|
||||||
|
// Define the touchHandler function
|
||||||
|
// ------------------------------------
|
||||||
|
touchHandler = function(zone, e) {
|
||||||
|
wasDragging = Object.assign({}, isDragging);
|
||||||
|
let boxTouched = false;
|
||||||
|
boxKeys.forEach((boxKey) => {
|
||||||
|
if (touchInText(e, boxes[boxKey], boxKey)) {
|
||||||
|
isDragging[boxKey] = true;
|
||||||
|
wasDragging[boxKey] = true;
|
||||||
|
boxTouched = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!boxTouched) {
|
||||||
|
if (!Object.values(isDragging).some(Boolean)) { // check if no boxes are being dragged
|
||||||
|
deselectAllBoxes();
|
||||||
|
if (doubleTapTimer) {
|
||||||
|
clearTimeout(doubleTapTimer);
|
||||||
|
doubleTapTimer = null;
|
||||||
|
// Save boxesConfig on double tap outside of any box and when no boxes are being dragged
|
||||||
|
Object.keys(boxPos).forEach((boxKey) => {
|
||||||
|
boxesConfig[boxKey].boxPos.x = (boxPos[boxKey].x / w).toFixed(3);
|
||||||
|
boxesConfig[boxKey].boxPos.y = (boxPos[boxKey].y / h).toFixed(3);
|
||||||
|
});
|
||||||
|
storage.write(fileName, JSON.stringify(boxesConfig));
|
||||||
|
displaySaveIcon();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if any box is being dragged, just deselect all without saving
|
||||||
|
deselectAllBoxes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.values(wasDragging).some(Boolean) || !boxTouched) {
|
||||||
|
draw(boxes);
|
||||||
|
}
|
||||||
|
doubleTapTimer = setTimeout(() => {
|
||||||
|
doubleTapTimer = null;
|
||||||
|
}, 500); // Increase or decrease this value based on the desired double tap timing
|
||||||
|
movementDistance = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Define the dragHandler function
|
||||||
|
// ------------------------------------
|
||||||
|
dragHandler = function(e) {
|
||||||
|
// Calculate the movement distance
|
||||||
|
movementDistance += Math.abs(e.dx) + Math.abs(e.dy);
|
||||||
|
// Check if the movement distance exceeds a threshold
|
||||||
|
if (movementDistance > 1) {
|
||||||
|
boxKeys.forEach((boxKey) => {
|
||||||
|
if (isDragging[boxKey]) {
|
||||||
|
widgets.hide();
|
||||||
|
let boxItem = boxes[boxKey];
|
||||||
|
calcBoxSize(boxItem);
|
||||||
|
let newX = boxPos[boxKey].x + e.dx;
|
||||||
|
let newY = boxPos[boxKey].y + e.dy;
|
||||||
|
if (newX - totalWidth / 2 >= 0 &&
|
||||||
|
newX + totalWidth / 2 <= w &&
|
||||||
|
newY - totalHeight / 2 >= 0 &&
|
||||||
|
newY + totalHeight / 2 <= h ) {
|
||||||
|
boxPos[boxKey].x = newX;
|
||||||
|
boxPos[boxKey].y = newY;
|
||||||
|
}
|
||||||
|
const pos = calcBoxPos(boxKey);
|
||||||
|
g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
draw(boxes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Bangle.on('touch', touchHandler);
|
||||||
|
Bangle.on('drag', dragHandler);
|
||||||
|
|
||||||
|
Bangle.setUI({
|
||||||
|
mode : "clock",
|
||||||
|
remove : function() {
|
||||||
|
// Remove event handlers, stop draw timer, remove custom font if used
|
||||||
|
Bangle.removeListener('touch', touchHandler);
|
||||||
|
Bangle.removeListener('drag', dragHandler);
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
delete Graphics.prototype.setFontBrunoAce;
|
||||||
|
// Restore original drawString function (no outlines)
|
||||||
|
g.drawString = g_drawString;
|
||||||
|
restoreSetColor();
|
||||||
|
widgets.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loadCustomFont();
|
||||||
|
draw(boxes);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* 11. Main execution part
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
widgets.swipeOn();
|
||||||
|
modSetColor();
|
||||||
|
setup();
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"time": {
|
||||||
|
"font": "BrunoAce",
|
||||||
|
"fontSize": 1,
|
||||||
|
"outline": 2,
|
||||||
|
"color": "#000",
|
||||||
|
"outlineColor": "#fff",
|
||||||
|
"border": "#000",
|
||||||
|
"xPadding": 0,
|
||||||
|
"yPadding": -4,
|
||||||
|
"xOffset": 0,
|
||||||
|
"yOffset": 3,
|
||||||
|
"boxPos": { "x": 0.633, "y": 0.16 }
|
||||||
|
},
|
||||||
|
"dow": {
|
||||||
|
"font": "6x8",
|
||||||
|
"fontSize": 2,
|
||||||
|
"outline": 1,
|
||||||
|
"color": "#000",
|
||||||
|
"outlineColor": "#fff",
|
||||||
|
"border": "#000",
|
||||||
|
"xPadding": -0.5,
|
||||||
|
"yPadding": 0.5,
|
||||||
|
"xOffset": 1,
|
||||||
|
"yOffset": 1,
|
||||||
|
"boxPos": { "x": 0.633, "y": 0.3 }
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"font": "6x8",
|
||||||
|
"fontSize": 1,
|
||||||
|
"outline": 1,
|
||||||
|
"color": "#000",
|
||||||
|
"outlineColor": "#fff",
|
||||||
|
"border": "#000",
|
||||||
|
"xPadding": 0,
|
||||||
|
"yPadding": 0.5,
|
||||||
|
"xOffset": 1,
|
||||||
|
"yOffset": 1,
|
||||||
|
"boxPos": { "x": 0.633, "y": 0.39 }
|
||||||
|
},
|
||||||
|
"batt": {
|
||||||
|
"font": "4x6",
|
||||||
|
"fontSize": 2,
|
||||||
|
"outline": 1,
|
||||||
|
"color": "#0ff",
|
||||||
|
"outlineColor": "#000",
|
||||||
|
"border": "#fff",
|
||||||
|
"xPadding": -0.5,
|
||||||
|
"yPadding": -0.5,
|
||||||
|
"xOffset": 2,
|
||||||
|
"yOffset": 1,
|
||||||
|
"boxPos": { "x": 0.92, "y": 0.95 }
|
||||||
|
},
|
||||||
|
"bg": {
|
||||||
|
"img": "boxclk.beachhouse.img"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkEogA/AEB5TCwVAC6aOCoED/4AQmAXDh4XR+AX5+URgERl4XR+KGEj4XP+cAgMz/8ziEAn4XOkEBCIfziECC5ouBl/zkMAiU/+QwGC4/wE4MgLwQFCQgoXHiEfO4Mj/8yO4PxgIXMHwMggb+DgRQBC5fygIPBn4xBiYFCiDDEC43xgfyLQJfCGoMvmDCEC5ABCVIJlBCoIXM+EPAIRgBAQIIDC542DC53xT4MfC4cCBAanLBQaWDBIgXo+YXXdgoXQbQcBd5YXHiAYHC5wAC+UQe4IXTDAMABAQXEgYPEiDQEAATdBAYMwC4ZUCK4aLHSoIACC5IuHSogXKCxAXHogXTCwQAFC5YUIC7b/EC6TFFC6IwJC5itBcwQXUbI7vBC5bFGAAgXMDBQXNJIQXUGBEEBog="))
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"id": "boxclk",
|
||||||
|
"name": "Box Clock",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background",
|
||||||
|
"icon": "app.png",
|
||||||
|
"screenshots": [
|
||||||
|
{"url":"screenshot.png"},
|
||||||
|
{"url":"screenshot-1.png"}
|
||||||
|
],
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"allow_emulator": true,
|
||||||
|
"storage": [
|
||||||
|
{"name":"boxclk.app.js","url":"app.js"},
|
||||||
|
{"name":"boxclk.settings.js","url":"settings.js"},
|
||||||
|
{"name":"boxclk.img","url":"icon.js","evaluate":true},
|
||||||
|
{"name":"boxclk.beachhouse.img","url":"beachhouse.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"boxclk.json","url":"boxclk.json"}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
|
|
@ -0,0 +1,87 @@
|
||||||
|
(function () {
|
||||||
|
let storage = require("Storage");
|
||||||
|
let fileRegex = /^boxclk-(\d+)\.json$/;
|
||||||
|
let selectedConfig;
|
||||||
|
let configs = {};
|
||||||
|
let hasDefaultConfig = false;
|
||||||
|
|
||||||
|
function getNextConfigNumber() {
|
||||||
|
let maxNumber = 0;
|
||||||
|
storage.list().forEach(file => {
|
||||||
|
let match = file.match(fileRegex);
|
||||||
|
if (match) {
|
||||||
|
let number = parseInt(match[1]);
|
||||||
|
if (number > maxNumber) {
|
||||||
|
maxNumber = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return maxNumber + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelection(config) {
|
||||||
|
return function () {
|
||||||
|
selectedConfig = config === "Default" ? 0 : config;
|
||||||
|
menu["Cfg:"].value = selectedConfig === 0 ? "Default" : selectedConfig;
|
||||||
|
E.showMenu(menu);
|
||||||
|
|
||||||
|
// Retrieve existing data and update selectedConfig
|
||||||
|
let defaultConfig = storage.readJSON("boxclk.json", 1) || {};
|
||||||
|
defaultConfig.selectedConfig = selectedConfig;
|
||||||
|
storage.writeJSON("boxclk.json", defaultConfig);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.list().forEach(file => {
|
||||||
|
let match = file.match(fileRegex);
|
||||||
|
if (match) {
|
||||||
|
let configNumber = match[1];
|
||||||
|
configs[configNumber] = handleSelection(configNumber);
|
||||||
|
} else if (file === "boxclk.json") {
|
||||||
|
hasDefaultConfig = true;
|
||||||
|
let defaultConfig = storage.readJSON(file, 1);
|
||||||
|
if (defaultConfig && defaultConfig.selectedConfig != null) {
|
||||||
|
// Check if corresponding config file exists
|
||||||
|
let configFileName = 'boxclk-' + defaultConfig.selectedConfig + '.json';
|
||||||
|
if (storage.read(configFileName)) {
|
||||||
|
// If it exists, assign selectedConfig
|
||||||
|
selectedConfig = defaultConfig.selectedConfig;
|
||||||
|
} else {
|
||||||
|
// If it does not exist, set selectedConfig to 0 and update boxclk.json
|
||||||
|
defaultConfig.selectedConfig = 0;
|
||||||
|
storage.writeJSON("boxclk.json", defaultConfig);
|
||||||
|
selectedConfig = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!selectedConfig) {
|
||||||
|
if (hasDefaultConfig) {
|
||||||
|
selectedConfig = "Default";
|
||||||
|
} else {
|
||||||
|
let nextConfigNumber = getNextConfigNumber();
|
||||||
|
selectedConfig = nextConfigNumber.toString();
|
||||||
|
configs[selectedConfig] = handleSelection(selectedConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let menu = {
|
||||||
|
'': { 'title': '-- Box Clock --' },
|
||||||
|
'< Back': () => Bangle.showClock(),
|
||||||
|
'Cfg:': {
|
||||||
|
value: selectedConfig === 0 ? "Default" : selectedConfig,
|
||||||
|
format: () => selectedConfig === 0 ? "Default" : selectedConfig
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasDefaultConfig) {
|
||||||
|
menu['Default'] = handleSelection('Default');
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(configs).forEach(config => {
|
||||||
|
menu[config] = handleSelection(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
E.showMenu(menu);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue