forked from espruino/BangleApps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ClockFace.js
147 lines (139 loc) · 4.98 KB
/
ClockFace.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
Most of the boilerplate needed to run a clock.
See ClockFace.md for documentation
*/
function ClockFace(options) {
if ("function"=== typeof options) options = {draw: options}; // simple usage
// some validation, in the hopes of at least catching typos/basic mistakes
Object.keys(options).forEach(k => {
if (![
"precision",
"init", "draw", "update",
"pause", "resume", "remove",
"up", "down", "upDown",
"settingsFile",
].includes(k)) throw `Invalid ClockFace option: ${k}`;
});
if (!options.draw && !options.update) throw "ClockFace needs at least one of draw() or update() functions";
this.draw = options.draw || (t=> {
options.update.apply(this, [t, {d: true, h: true, m: true, s: true}]);
});
this.update = options.update || (t => {
g.clearRect(Bangle.appRect);
options.draw.apply(this, [t, {d: true, h: true, m: true, s: true}]);
});
if (options.precision===1000||options.precision===60000) throw "ClockFace precision is in seconds, not ms";
this.precision = (options.precision || 60);
if (options.init) this.init = options.init;
if (options.pause) this._pause = options.pause;
if (options.resume) this._resume = options.resume;
if (options.remove) this._remove = options.remove;
if ((options.up || options.down) && options.upDown) throw "ClockFace up/down and upDown cannot be used together";
if (options.up || options.down) this._upDown = (dir) => {
if (dir<0 && options.up) options.up.apply(this);
if (dir>0 && options.down) options.down.apply(this);
};
if (options.upDown) this._upDown = options.upDown;
if (options.settingsFile) {
const settings = (require("Storage").readJSON(options.settingsFile, true) || {});
Object.keys(settings).forEach(k => {
this[k] = settings[k];
});
}
// showDate defaults to true
if (this.showDate===undefined) this.showDate = true;
// if (old) setting was to not load widgets, default to hiding them
if (this.hideWidgets===undefined && this.loadWidgets===false) this.hideWidgets = 1;
let s = require("Storage").readJSON("setting.json",1)||{};
// use global 24/12-hour setting if not set by clock-settings
if (!('is12Hour' in this)) this.is12Hour = !!(s["12hour"]);
}
ClockFace.prototype.tick = function() {
"ram"
if (this._removed) return;
const time = new Date();
const now = {
d: `${time.getFullYear()}-${time.getMonth()}-${time.getDate()}`,
h: time.getHours(),
m: time.getMinutes(),
s: time.getSeconds(),
};
if (!this._last) {
g.clear(true);
if (global.WIDGETS) Bangle.drawWidgets();
g.reset();
this.draw.apply(this, [time, {d: true, h: true, m: true, s: true}]);
} else {
let c = {d: false, h: false, m: false, s: false}; // changed
if (now.d!==this._last.d) c.d = c.h = c.m = c.s = true;
else if (now.h!==this._last.h) c.h = c.m = c.s = true;
else if (now.m!==this._last.m) c.m = c.s = true;
else if (now.s!==this._last.s) c.s = true;
g.reset();
this.update.apply(this, [time, c]);
}
this._last = now;
if (this.paused) return; // called redraw() while still paused
// figure out timeout: if e.g. precision=60s, update at the start of a new minute
const interval = this.precision*1000;
this._timeout = setTimeout(() => this.tick(), interval-(Date.now()%interval));
};
ClockFace.prototype.start = function() {
/* Some widgets want to know if we're in a clock or not (like chrono, widget clock, etc). Normally
.CLOCK is set by Bangle.setUI('clock') but we want to load widgets so we can check appRect and *then*
call setUI. see #1864 */
Bangle.CLOCK = 1;
Bangle.loadWidgets();
const widget_util = ["show", "hide", "swipeOn"][this.hideWidgets|0];
require("widget_utils")[widget_util]();
if (this.init) this.init.apply(this);
const uiRemove = this._remove ? () => this.remove() : undefined;
if (this._upDown) {
Bangle.setUI({
mode: "clockupdown",
remove: uiRemove,
}, d => this._upDown.apply(this, [d]));
} else {
Bangle.setUI({
mode: "clock",
remove: uiRemove,
});
}
delete this._last;
this.paused = false;
this.tick();
this._onLcd = on => {
if (on) this.resume();
else this.pause();
};
Bangle.on("lcdPower", this._onLcd);
};
ClockFace.prototype.pause = function() {
if (!this._timeout) return; // already paused
clearTimeout(this._timeout);
delete this._timeout;
this.paused = true; // apps might want to check this
if (this._pause) this._pause.apply(this);
};
ClockFace.prototype.resume = function() {
if (this._timeout) return; // not paused
delete this._last;
this.paused = false;
if (this._resume) this._resume.apply(this);
this.tick();
};
ClockFace.prototype.remove = function() {
this._removed = true;
require("widget_utils").show();
if (this._timeout) clearTimeout(this._timeout);
Bangle.removeListener("lcdPower", this._onLcd);
if (this._remove) this._remove.apply(this);
};
/**
* Force a complete redraw
*/
ClockFace.prototype.redraw = function() {
delete this._last;
this.tick();
};
exports = ClockFace;