Skip to content

Commit 4249f63

Browse files
authored
Merge pull request #2349 from peerdavid/master
[AIClock] Added clkinfos
2 parents 5f35b3a + 3673a0d commit 4249f63

18 files changed

+270
-50
lines changed

apps/aiclock/ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
0.02: Design improvements and fixes.
33
0.03: Indicate battery level through line occurrence.
44
0.04: Use widget_utils module.
5+
0.05: Support for clkinfo.

apps/aiclock/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ The original output of stable diffusion is shown here:
1010

1111
My implementation is shown below. Note that horizontal lines occur randomly, but the
1212
probability is correlated with the battery level. So if your screen contains only
13-
a few lines its time to charge your bangle again ;)
13+
a few lines its time to charge your bangle again ;) Also note that the upper text
14+
implementes the clkinfo module and can be configured via touch left/right/up/down.
15+
Touch at the center to trigger the selected action.
1416

1517
![](impl.png)
1618

apps/aiclock/aiclock.app.js

Lines changed: 249 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1-
/**
1+
/************************************************
22
* AI Clock
33
*/
4+
const storage = require('Storage');
5+
const clock_info = require("clock_info");
6+
7+
8+
9+
/************************************************
10+
* Assets
11+
*/
412
require("Font7x11Numeric7Seg").add(Graphics);
513
Graphics.prototype.setFontGochiHand = function(scale) {
614
// Actual height 27 (29 - 3)
@@ -13,14 +21,128 @@ Graphics.prototype.setFontGochiHand = function(scale) {
1321
return this;
1422
}
1523

16-
/*
24+
/************************************************
1725
* Set some important constants such as width, height and center
1826
*/
1927
var W = g.getWidth(),R=W/2;
2028
var H = g.getHeight();
2129
var cx = W/2;
2230
var cy = H/2;
2331
var drawTimeout;
32+
var lock_input = false;
33+
34+
35+
/************************************************
36+
* SETTINGS
37+
*/
38+
const SETTINGS_FILE = "aiclock.setting.json";
39+
let settings = {
40+
menuPosX: 0,
41+
menuPosY: 0,
42+
};
43+
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
44+
for (const key in saved_settings) {
45+
settings[key] = saved_settings[key]
46+
}
47+
48+
49+
/************************************************
50+
* Menu
51+
*/
52+
function getDate(){
53+
var date = new Date();
54+
return ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2)
55+
}
56+
57+
58+
// Custom clockItems menu - therefore, its added here and not in a clkinfo.js file.
59+
var clockItems = {
60+
name: getDate(),
61+
img: null,
62+
items: [
63+
{ name: "Week",
64+
get: () => ({ text: "Week " + weekOfYear(), img: null}),
65+
show: function() { clockItems.items[0].emit("redraw"); },
66+
hide: function () {}
67+
},
68+
]
69+
};
70+
71+
function weekOfYear() {
72+
var date = new Date();
73+
date.setHours(0, 0, 0, 0);
74+
// Thursday in current week decides the year.
75+
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
76+
// January 4 is always in week 1.
77+
var week1 = new Date(date.getFullYear(), 0, 4);
78+
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
79+
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
80+
- 3 + (week1.getDay() + 6) % 7) / 7);
81+
}
82+
83+
84+
85+
// Load menu
86+
var menu = clock_info.load();
87+
menu = menu.concat(clockItems);
88+
89+
90+
// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it.
91+
if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){
92+
settings.menuPosX = 0;
93+
settings.menuPosY = 0;
94+
}
95+
96+
// Set draw functions for each item
97+
menu.forEach((menuItm, x) => {
98+
menuItm.items.forEach((item, y) => {
99+
function drawItem() {
100+
// For the clock, we have a special case, as we don't wanna redraw
101+
// immediately when something changes. Instead, we update data each minute
102+
// to save some battery etc. Therefore, we hide (and disable the listener)
103+
// immedeately after redraw...
104+
item.hide();
105+
106+
// After drawing the item, we enable inputs again...
107+
lock_input = false;
108+
109+
var info = item.get();
110+
drawMenuItem(info.text, info.img);
111+
}
112+
113+
item.on('redraw', drawItem);
114+
})
115+
});
116+
117+
118+
function canRunMenuItem(){
119+
if(settings.menuPosY == 0){
120+
return false;
121+
}
122+
123+
var menuEntry = menu[settings.menuPosX];
124+
var item = menuEntry.items[settings.menuPosY-1];
125+
return item.run !== undefined;
126+
}
127+
128+
129+
function runMenuItem(){
130+
if(settings.menuPosY == 0){
131+
return;
132+
}
133+
134+
var menuEntry = menu[settings.menuPosX];
135+
var item = menuEntry.items[settings.menuPosY-1];
136+
try{
137+
var ret = item.run();
138+
if(ret){
139+
Bangle.buzz(300, 0.6);
140+
}
141+
} catch (ex) {
142+
// Simply ignore it...
143+
}
144+
}
145+
24146

25147
/*
26148
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/
@@ -76,7 +198,50 @@ function toAngle(a){
76198
return a
77199
}
78200

201+
202+
function drawMenuItem(text, image){
203+
if(text == null){
204+
drawTime();
205+
return
206+
}
207+
// image = atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==");
208+
209+
text = String(text);
210+
211+
g.reset().setBgColor("#fff").setColor("#000");
212+
g.setFontAlign(0,0);
213+
g.setFont("Vector", 20);
214+
215+
var imgWidth = image == null ? 0 : 24;
216+
var strWidth = g.stringWidth(text);
217+
var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2);
218+
var w = imgWidth + strWidth;
219+
220+
g.clearRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2)
221+
222+
// Draw right line as designed by stable diffusion
223+
g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2);
224+
g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2);
225+
g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2);
226+
227+
// And finally the text
228+
g.drawString(text, cx+imgWidth/2, 42);
229+
g.drawString(text, cx+1+imgWidth/2, 41);
230+
231+
if(image != null) {
232+
var scale = image.width ? imgWidth / image.width : 1;
233+
g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale});
234+
}
235+
236+
drawTime();
237+
}
238+
239+
79240
function drawTime(){
241+
// Draw digital time first
242+
drawDigits();
243+
244+
// And now the analog time
80245
var drawHourHand = g.drawRotRect.bind(g,8,12,R-38);
81246
var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 );
82247

@@ -90,42 +255,13 @@ function drawTime(){
90255
h += date.getMinutes()/60.0;
91256
h = parseInt(h*360/12);
92257

93-
// Draw minute and hour bg
94-
g.setColor(g.theme.bg);
95-
drawHourHand(toAngle(h-3));
96-
drawHourHand(toAngle(h+3));
97-
drawMinuteHand(toAngle(m-2));
98-
drawMinuteHand(toAngle(m+3));
99-
100258
// Draw minute and hour fg
101259
g.setColor(g.theme.fg);
102260
drawHourHand(h);
103261
drawMinuteHand(m);
104262
}
105263

106264

107-
108-
function drawDate(){
109-
var date = new Date();
110-
g.setFontAlign(0,0);
111-
g.setFontGochiHand();
112-
113-
var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2);
114-
var w = g.stringWidth(text);
115-
g.setColor(g.theme.bg);
116-
g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12);
117-
118-
g.setColor(g.theme.fg);
119-
// Draw right line as designed by stable diffusion
120-
g.drawLine(cx+w/2+5, 20, cx+w/2+5, 40+12);
121-
g.drawLine(cx+w/2+6, 20, cx+w/2+6, 40+12);
122-
g.drawLine(cx+w/2+7, 20, cx+w/2+7, 40+12);
123-
124-
// And finally the text
125-
g.drawString(text, cx, 40);
126-
}
127-
128-
129265
function drawDigits(){
130266
var date = new Date();
131267

@@ -156,20 +292,35 @@ function drawDigits(){
156292
}
157293

158294

295+
function drawDate(){
296+
var menuEntry = menu[settings.menuPosX];
297+
298+
// The first entry is the overview...
299+
if(settings.menuPosY == 0){
300+
drawMenuItem(menuEntry.name, menuEntry.img);
301+
return;
302+
}
303+
304+
// Draw item if needed
305+
lock_input = true;
306+
var item = menuEntry.items[settings.menuPosY-1];
307+
item.show();
308+
}
309+
310+
311+
312+
159313

160314
function draw(){
161315
// Queue draw in one minute
162316
queueDraw();
163317

164-
165318
g.reset();
166319
g.clearRect(0, 0, g.getWidth(), g.getHeight());
167-
168320
g.setColor(1,1,1);
321+
169322
drawBackground();
170323
drawDate();
171-
drawDigits();
172-
drawTime();
173324
drawCircle(Bangle.isLocked());
174325
}
175326

@@ -190,6 +341,68 @@ Bangle.on('lock', function(isLocked) {
190341
drawCircle(isLocked);
191342
});
192343

344+
Bangle.on('touch', function(btn, e){
345+
var left = parseInt(g.getWidth() * 0.22);
346+
var right = g.getWidth() - left;
347+
var upper = parseInt(g.getHeight() * 0.22);
348+
var lower = g.getHeight() - upper;
349+
350+
var is_upper = e.y < upper;
351+
var is_lower = e.y > lower;
352+
var is_left = e.x < left && !is_upper && !is_lower;
353+
var is_right = e.x > right && !is_upper && !is_lower;
354+
var is_center = !is_upper && !is_lower && !is_left && !is_right;
355+
356+
if(lock_input){
357+
return;
358+
}
359+
360+
if(is_lower){
361+
Bangle.buzz(40, 0.6);
362+
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
363+
364+
draw();
365+
}
366+
367+
if(is_upper){
368+
Bangle.buzz(40, 0.6);
369+
settings.menuPosY = settings.menuPosY-1;
370+
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY;
371+
372+
draw();
373+
}
374+
375+
if(is_right){
376+
Bangle.buzz(40, 0.6);
377+
settings.menuPosX = (settings.menuPosX+1) % menu.length;
378+
settings.menuPosY = 0;
379+
draw();
380+
}
381+
382+
if(is_left){
383+
Bangle.buzz(40, 0.6);
384+
settings.menuPosY = 0;
385+
settings.menuPosX = settings.menuPosX-1;
386+
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
387+
draw();
388+
}
389+
390+
if(is_center){
391+
if(canRunMenuItem()){
392+
runMenuItem();
393+
}
394+
}
395+
});
396+
397+
398+
E.on("kill", function(){
399+
try{
400+
storage.write(SETTINGS_FILE, settings);
401+
} catch(ex){
402+
// If this fails, we still kill the app...
403+
}
404+
});
405+
193406

194407
/*
195408
* Some helpers
@@ -203,7 +416,6 @@ function queueDraw() {
203416
}
204417

205418

206-
207419
/*
208420
* Lets start widgets, listen for btn etc.
209421
*/
@@ -216,6 +428,7 @@ Bangle.loadWidgets();
216428
* area to the top bar doesn't get cleared.
217429
*/
218430
require('widget_utils').hide();
431+
219432
// Clear the screen once, at startup and draw clock
220433
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
221434
draw();

apps/aiclock/impl.png

-146 Bytes
Loading

apps/aiclock/impl_2.png

2.4 KB
Loading

apps/aiclock/impl_3.png

2.43 KB
Loading

0 commit comments

Comments
 (0)