AI Clock - Use clock_info.addInteractive instead of a custom implementation

master
David Peer 2023-01-05 22:05:52 +01:00
parent bdefe4217c
commit 3c1465ae46
5 changed files with 96 additions and 245 deletions

View File

@ -4,3 +4,4 @@
0.04: Use widget_utils module. 0.04: Use widget_utils module.
0.05: Support for clkinfo. 0.05: Support for clkinfo.
0.06: ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc. 0.06: ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc.
0.07: Use clock_info.addInteractive instead of a custom implementation

View File

@ -11,8 +11,7 @@ The original output of stable diffusion is shown here:
My implementation is shown below. Note that horizontal lines occur randomly, but the My implementation is shown below. Note that horizontal lines occur randomly, but the
probability is correlated with the battery level. So if your screen contains only probability is correlated with the battery level. So if your screen contains only
a few lines its time to charge your bangle again ;) Also note that the upper text a few lines its time to charge your bangle again ;) Also note that the upper text
implementes the clkinfo module and can be configured via touch left/right/up/down. implements the clkinfo module and can be configured via touch and swipe left/right and up/down.
Touch at the center to trigger the selected action.
![](impl.png) ![](impl.png)

View File

@ -1,7 +1,6 @@
/************************************************ /************************************************
* AI Clock * AI Clock
*/ */
const storage = require('Storage');
const clock_info = require("clock_info"); const clock_info = require("clock_info");
@ -21,124 +20,14 @@ Graphics.prototype.setFontGochiHand = function(scale) {
return this; return this;
} }
/************************************************
* Set some important constants such as width, height and center
*/
var W = g.getWidth(),R=W/2;
var H = g.getHeight();
var cx = W/2;
var cy = H/2;
var drawTimeout;
function drawBackground(start, end) {
/************************************************
* SETTINGS
*/
const SETTINGS_FILE = "aiclock.setting.json";
let settings = {
menuPosX: 0,
menuPosY: 0,
};
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
settings[key] = saved_settings[key]
}
/************************************************
* Menu
*/
function getDate(){
var date = new Date();
return ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2)
}
// Custom clockItems menu - therefore, its added here and not in a clkinfo.js file.
var clockItems = {
name: getDate(),
img: null,
items: [
{ name: "Week",
get: () => ({ text: "Week " + weekOfYear(), img: null}),
show: function() { clockItems.items[0].emit("redraw"); },
hide: function () {}
},
]
};
function weekOfYear() {
var date = new Date();
date.setHours(0, 0, 0, 0);
// Thursday in current week decides the year.
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
// January 4 is always in week 1.
var week1 = new Date(date.getFullYear(), 0, 4);
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
- 3 + (week1.getDay() + 6) % 7) / 7);
}
// Load menu
var menu = clock_info.load();
menu = menu.concat(clockItems);
// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it.
if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){
settings.menuPosX = 0;
settings.menuPosY = 0;
}
function canRunMenuItem(){
if(settings.menuPosY == 0){
return false;
}
var menuEntry = menu[settings.menuPosX];
var item = menuEntry.items[settings.menuPosY-1];
return item.run !== undefined;
}
function runMenuItem(){
if(settings.menuPosY == 0){
return;
}
var menuEntry = menu[settings.menuPosX];
var item = menuEntry.items[settings.menuPosY-1];
try{
var ret = item.run();
if(ret){
Bangle.buzz(300, 0.6);
}
} catch (ex) {
// Simply ignore it...
}
}
/*
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/
*/
Graphics.prototype.drawRotRect = function(w, r1, r2, angle) {
angle = angle % 360;
var w2=w/2, h=r2-r1, theta=angle*Math.PI/180;
return this.fillPoly(this.transformVertices([-w2,0,-w2,-h,w2,-h,w2,0],
{x:cx+r1*Math.sin(theta),y:cy-r1*Math.cos(theta),rotate:theta}));
};
function drawBackground() {
g.setFontAlign(0,0); g.setFontAlign(0,0);
g.setColor(g.theme.fg); g.setColor("#000");
var bat = E.getBattery() / 100.0; var bat = E.getBattery() / 100.0;
var y = 0; var y = start;
while(y < H){ while(y < end){
// Show less lines in case of small battery level. // Show less lines in case of small battery level.
if(Math.random() > bat){ if(Math.random() > bat){
y += 5; y += 5;
@ -154,6 +43,30 @@ function drawBackground() {
} }
/************************************************
* Set some important constants such as width, height and center
*/
var W = g.getWidth(),R=W/2;
var H = g.getHeight();
var cx = W/2;
var cy = H/2;
var drawTimeout;
var clkInfoY = 60;
/*
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/
*/
Graphics.prototype.drawRotRect = function(w, r1, r2, angle) {
angle = angle % 360;
var w2=w/2, h=r2-r1, theta=angle*Math.PI/180;
return this.fillPoly(this.transformVertices([-w2,0,-w2,-h,w2,-h,w2,0],
{x:cx+r1*Math.sin(theta),y:cy-r1*Math.cos(theta),rotate:theta}));
};
function drawCircle(isLocked){ function drawCircle(isLocked){
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
g.fillCircle(cx, cy, 12); g.fillCircle(cx, cy, 12);
@ -163,54 +76,6 @@ function drawCircle(isLocked){
g.fillCircle(cx, cy, 6); g.fillCircle(cx, cy, 6);
} }
function toAngle(a){
if (a < 0){
return 360 + a;
}
if(a > 360) {
return 360 - a;
}
return a
}
function drawMenuItem(text, image){
if(text == null){
drawTime();
return
}
text = String(text);
g.reset().setBgColor("#fff").setColor("#000");
g.setFontAlign(0,0);
g.setFont("Vector", 20);
var imgWidth = image == null ? 0 : 24;
var strWidth = g.stringWidth(text);
var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2);
var w = imgWidth + strWidth;
g.clearRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2)
// Draw right line as designed by stable diffusion
g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2);
g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2);
g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2);
// And finally the text
g.drawString(text, cx+imgWidth/2, 42);
g.drawString(text, cx+1+imgWidth/2, 41);
if(image != null) {
var scale = image.width ? imgWidth / image.width : 1;
g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale});
}
drawTime();
}
function drawTime(){ function drawTime(){
// Draw digital time first // Draw digital time first
@ -267,34 +132,23 @@ function drawDigits(){
} }
function drawMenu(){ function draw(){
var menuEntry = menu[settings.menuPosX]; // Note that we force a redraw also of the clock info as
// we want to ensure (for design purpose) that the hands
// The first entry is the overview... // are above the clkinfo section.
if(settings.menuPosY == 0){ clockInfoMenu.redraw();
drawMenuItem(menuEntry.name, menuEntry.img);
return;
}
// Draw item if needed
var item = menuEntry.items[settings.menuPosY-1].get();
drawMenuItem(item.text, item.img);
} }
function drawMainClock(){
function draw(){
// Queue draw in one minute // Queue draw in one minute
queueDraw(); queueDraw();
g.reset(); g.setColor("#fff");
g.clearRect(0, 0, g.getWidth(), g.getHeight()); g.reset().clearRect(0, clkInfoY, g.getWidth(), g.getHeight());
g.setColor(1,1,1);
drawBackground(); drawBackground(clkInfoY, H);
drawMenu(); drawTime();
drawCircle(Bangle.isLocked()); drawCircle(Bangle.isLocked());
} }
@ -304,7 +158,7 @@ function draw(){
*/ */
Bangle.on('lcdPower',on=>{ Bangle.on('lcdPower',on=>{
if (on) { if (on) {
draw(true); draw();
} else { // stop draw timer } else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined; drawTimeout = undefined;
@ -315,62 +169,10 @@ Bangle.on('lock', function(isLocked) {
drawCircle(isLocked); drawCircle(isLocked);
}); });
Bangle.on('touch', function(btn, e){
var left = parseInt(g.getWidth() * 0.22);
var right = g.getWidth() - left;
var upper = parseInt(g.getHeight() * 0.22);
var lower = g.getHeight() - upper;
var is_upper = e.y < upper;
var is_lower = e.y > lower;
var is_left = e.x < left && !is_upper && !is_lower;
var is_right = e.x > right && !is_upper && !is_lower;
var is_center = !is_upper && !is_lower && !is_left && !is_right;
if(is_lower){
Bangle.buzz(40, 0.6);
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
draw();
}
if(is_upper){
Bangle.buzz(40, 0.6);
settings.menuPosY = settings.menuPosY-1;
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY;
draw();
}
if(is_right){
Bangle.buzz(40, 0.6);
settings.menuPosX = (settings.menuPosX+1) % menu.length;
settings.menuPosY = 0;
draw();
}
if(is_left){
Bangle.buzz(40, 0.6);
settings.menuPosY = 0;
settings.menuPosX = settings.menuPosX-1;
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
draw();
}
if(is_center){
if(canRunMenuItem()){
runMenuItem();
}
}
});
E.on("kill", function(){ E.on("kill", function(){
try{ clockInfoMenu.remove();
storage.write(SETTINGS_FILE, settings); delete clockInfoMenu;
} catch(ex){
// If this fails, we still kill the app...
}
}); });
@ -386,6 +188,55 @@ function queueDraw() {
} }
/************************************************
* Clock Info
*/
let clockInfoItems = clock_info.load();
let clockInfoMenu = clock_info.addInteractive(clockInfoItems, {
x : 0,
y: 0,
w: W,
h: clkInfoY,
draw : (itm, info, options) => {
g.setFontAlign(0,0);
g.setFont("Vector", 20);
g.setColor("#fff");
g.fillRect(options.x, options.y, options.x+options.w, options.y+options.h);
drawBackground(0, clkInfoY+2);
// Set text and font
var image = info.img;
var text = String(info.text);
var imgWidth = image == null ? 0 : 24;
var strWidth = g.stringWidth(text);
var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2);
var w = imgWidth + strWidth;
// Draw right line as designed by stable diffusion
g.setColor(options.focus ? "#0f0" : "#fff");
g.fillRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2)
g.setColor("#000");
g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2);
g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2);
g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2);
// Draw text and image
g.drawString(text, cx+imgWidth/2, 42);
g.drawString(text, cx+1+imgWidth/2, 41);
if(image != null) {
var scale = image.width ? imgWidth / image.width : 1;
g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale});
}
drawMainClock();
}
});
/* /*
* Lets start widgets, listen for btn etc. * Lets start widgets, listen for btn etc.
*/ */
@ -400,7 +251,7 @@ Bangle.loadWidgets();
require('widget_utils').hide(); require('widget_utils').hide();
// Clear the screen once, at startup and draw clock // Clear the screen once, at startup and draw clock
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear(); g.setTheme({bg:"#fff",fg:"#000",dark:false});
draw(); draw();
// After drawing the watch face, we can draw the widgets // After drawing the watch face, we can draw the widgets

View File

@ -3,7 +3,7 @@
"name": "AI Clock", "name": "AI Clock",
"shortName":"AI Clock", "shortName":"AI Clock",
"icon": "aiclock.png", "icon": "aiclock.png",
"version":"0.06", "version":"0.07",
"readme": "README.md", "readme": "README.md",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.", "description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",

View File

@ -97,7 +97,7 @@ let imgLock = function() {
/************************************************ /************************************************
* Menu * Clock Info
*/ */
let clockInfoItems = clock_info.load(); let clockInfoItems = clock_info.load();
let clockInfoMenu = clock_info.addInteractive(clockInfoItems, { let clockInfoMenu = clock_info.addInteractive(clockInfoItems, {