1
0
Fork 0

Merge branch 'espruino:master' into master

master
stweedo 2023-05-09 04:49:59 -05:00 committed by GitHub
commit ef29ff3709
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 338 additions and 98 deletions

View File

@ -13,3 +13,4 @@
0.12: Mark dated events on a day 0.12: Mark dated events on a day
0.13: Switch to swipe left/right for month and up/down for year selection 0.13: Switch to swipe left/right for month and up/down for year selection
Display events for current month on touch Display events for current month on touch
0.14: Add support for holidays

View File

@ -10,6 +10,7 @@ Basic calendar
- Swipe down (Bangle.js 2 only) to go to the next year - Swipe down (Bangle.js 2 only) to go to the next year
- Touch to display events for current month - Touch to display events for current month
- Press the button (button 3 on Bangle.js 1) to exit - Press the button (button 3 on Bangle.js 1) to exit
- Holidays have same color as weekends and can be edited with the 'Download'-interface, e.g. by uploading an iCalendar file.
## Settings ## Settings

View File

@ -16,6 +16,7 @@ const white = "#ffffff";
const red = "#d41706"; const red = "#d41706";
const blue = "#0000ff"; const blue = "#0000ff";
const yellow = "#ffff00"; const yellow = "#ffff00";
const cyan = "#00ffff";
let bgColor = color4; let bgColor = color4;
let bgColorMonth = color1; let bgColorMonth = color1;
let bgColorDow = color2; let bgColorDow = color2;
@ -23,6 +24,7 @@ let bgColorWeekend = color3;
let fgOtherMonth = gray1; let fgOtherMonth = gray1;
let fgSameMonth = white; let fgSameMonth = white;
let bgEvent = blue; let bgEvent = blue;
let bgOtherEvent = "#ff8800";
const eventsPerDay=6; // how much different events per day we can display const eventsPerDay=6; // how much different events per day we can display
const date = new Date(); const date = new Date();
@ -36,9 +38,17 @@ const events = (require("Storage").readJSON("sched.json",1) || []).filter(a => a
date.setHours(time.h); date.setHours(time.h);
date.setMinutes(time.m); date.setMinutes(time.m);
date.setSeconds(time.s); date.setSeconds(time.s);
return {date: date, msg: a.msg}; return {date: date, msg: a.msg, type: "e"};
});
// add holidays & other events
(require("Storage").readJSON("calendar.days.json",1) || []).forEach(d => {
const date = new Date(d.date);
const o = {date: date, msg: d.name, type: d.type};
if (d.repeat) {
o.repeat = d.repeat;
}
events.push(o);
}); });
events.sort((a,b) => a.date - b.date);
if (settings.ndColors === undefined) { if (settings.ndColors === undefined) {
settings.ndColors = !g.theme.dark; settings.ndColors = !g.theme.dark;
@ -52,68 +62,16 @@ if (settings.ndColors === true) {
fgOtherMonth = blue; fgOtherMonth = blue;
fgSameMonth = black; fgSameMonth = black;
bgEvent = color2; bgEvent = color2;
bgOtherEvent = cyan;
} }
function getDowLbls(locale) { function getDowLbls(locale) {
let dowLbls; let days = startOnSun ? [0, 1, 2, 3, 4, 5, 6] : [1, 2, 3, 4, 5, 6, 0];
//TODO: Find some clever way to generate this programmatically from locale lib const d = new Date();
switch (locale) { return days.map(i => {
case "de_AT": d.setDate(d.getDate() + (i + 7 - d.getDay()) % 7);
case "de_CH": return require("locale").dow(d, 1);
case "de_DE": });
if (startOnSun) {
dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
} else {
dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
}
break;
case "nl_NL":
if (startOnSun) {
dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"];
} else {
dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"];
}
break;
case "fr_BE":
case "fr_CH":
case "fr_FR":
if (startOnSun) {
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
} else {
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
}
break;
case "sv_SE":
if (startOnSun) {
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
} else {
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
}
break;
case "it_CH":
case "it_IT":
if (startOnSun) {
dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"];
} else {
dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
}
break;
case "oc_FR":
if (startOnSun) {
dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"];
} else {
dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"];
}
break;
default:
if (startOnSun) {
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
} else {
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
}
break;
}
return dowLbls;
} }
function sameDay(d1, d2) { function sameDay(d1, d2) {
@ -206,8 +164,13 @@ function drawCalendar(date) {
weekBeforeMonth.setDate(weekBeforeMonth.getDate() - 7); weekBeforeMonth.setDate(weekBeforeMonth.getDate() - 7);
const week2AfterMonth = new Date(date.getFullYear(), date.getMonth()+1, 0); const week2AfterMonth = new Date(date.getFullYear(), date.getMonth()+1, 0);
week2AfterMonth.setDate(week2AfterMonth.getDate() + 14); week2AfterMonth.setDate(week2AfterMonth.getDate() + 14);
events.forEach(ev => {
if (ev.repeat === "y") {
ev.date.setFullYear(ev.date.getMonth() < 6 ? week2AfterMonth.getFullYear() : weekBeforeMonth.getFullYear());
}
});
const eventsThisMonth = events.filter(ev => ev.date > weekBeforeMonth && ev.date < week2AfterMonth); const eventsThisMonth = events.filter(ev => ev.date > weekBeforeMonth && ev.date < week2AfterMonth);
eventsThisMonth.sort((a,b) => a.date - b.date);
let i = 0; let i = 0;
for (y = 0; y < rowN - 1; y++) { for (y = 0; y < rowN - 1; y++) {
for (x = 0; x < colN; x++) { for (x = 0; x < colN; x++) {
@ -220,6 +183,33 @@ function drawCalendar(date) {
const y1 = y * rowH + headerH + rowH; const y1 = y * rowH + headerH + rowH;
const x2 = x * colW + colW; const x2 = x * colW + colW;
const y2 = y * rowH + headerH + rowH + rowH; const y2 = y * rowH + headerH + rowH + rowH;
if (eventsThisMonth.length > 0) {
// Display events for this day
eventsThisMonth.forEach((ev, idx) => {
if (sameDay(ev.date, curDay)) {
switch(ev.type) {
case "e": // alarm/event
const hour = ev.date.getHours() + ev.date.getMinutes()/60.0;
const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59
const height = (y2-2) - (y1+2); // height of a cell
const sliceHeight = height/eventsPerDay;
const ystart = (y1+2) + slice*sliceHeight;
g.setColor(bgEvent).fillRect(x1+1, ystart, x2-2, ystart+sliceHeight);
break;
case "h": // holiday
g.setColor(bgColorWeekend).fillRect(x1+1, y1+1, x2-1, y2-1);
break;
case "o": // other
g.setColor(bgOtherEvent).fillRect(x1+1, y1+1, x2-1, y2-1);
break;
}
eventsThisMonth.splice(idx, 1); // this event is no longer needed
}
});
}
if (isToday) { if (isToday) {
g.setColor(red); g.setColor(red);
g.drawRect(x1, y1, x2, y2); g.drawRect(x1, y1, x2, y2);
@ -231,23 +221,6 @@ function drawCalendar(date) {
); );
} }
if (eventsThisMonth.length > 0) {
// Display events for this day
g.setColor(bgEvent);
eventsThisMonth.forEach((ev, idx) => {
if (sameDay(ev.date, curDay)) {
const hour = ev.date.getHours() + ev.date.getMinutes()/60.0;
const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59
const height = (y2-2) - (y1+2); // height of a cell
const sliceHeight = height/eventsPerDay;
const ystart = (y1+2) + slice*sliceHeight;
g.fillRect(x1+1, ystart, x2-2, ystart+sliceHeight);
eventsThisMonth.splice(idx, 1); // this event is no longer needed
}
});
}
require("Font8x12").add(Graphics); require("Font8x12").add(Graphics);
g.setFont("8x12", fontSize); g.setFont("8x12", fontSize);
g.setColor(day < 50 ? fgOtherMonth : fgSameMonth); g.setColor(day < 50 ? fgOtherMonth : fgSameMonth);
@ -286,10 +259,11 @@ function setUI() {
}, },
btn: (n) => n === (process.env.HWVERSION === 2 ? 1 : 3) && load(), btn: (n) => n === (process.env.HWVERSION === 2 ? 1 : 3) && load(),
touch: (n,e) => { touch: (n,e) => {
events.sort((a,b) => a.date - b.date);
const menu = events.filter(ev => ev.date.getFullYear() === date.getFullYear() && ev.date.getMonth() === date.getMonth()).map(e => { const menu = events.filter(ev => ev.date.getFullYear() === date.getFullYear() && ev.date.getMonth() === date.getMonth()).map(e => {
const dateStr = require("locale").date(e.date, 1); const dateStr = require("locale").date(e.date, 1);
const timeStr = require("locale").time(e.date, 1); const timeStr = require("locale").time(e.date, 1);
return { title: `${dateStr} ${timeStr}` + (e.msg ? " " + e.msg : "") }; return { title: `${dateStr} ${e.type === "e" ? timeStr : ""}` + (e.msg ? " " + e.msg : "") };
}); });
if (menu.length === 0) { if (menu.length === 0) {
menu.push({title: /*LANG*/"No events"}); menu.push({title: /*LANG*/"No events"});

View File

@ -0,0 +1,234 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
<script src="../../core/lib/interface.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ical.js/1.5.0/ical.min.js"></script>
<script>
let dataElement = document.getElementById("data");
let holidays;
function sameDay(d1, d2) {
return d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate();
}
function render() {
holidays.sort((a,b) => new Date(a.date) - new Date(b.date));
document.getElementById('events').innerHTML = "";
holidays.forEach(holiday => {
renderHoliday(holiday);
});
}
function readFile(input) {
document.getElementById('upload').disabled = true;
for(let i=0; i<input.files.length; i++) {
const reader = new FileReader();
reader.addEventListener("load", () => {
const jCalData = ICAL.parse(reader.result);
const comp = new ICAL.Component(jCalData);
// Fetch the VEVENT part
comp.getAllSubcomponents('vevent').forEach(vevent => {
event = new ICAL.Event(vevent);
holidays = holidays.filter(holiday => !sameDay(new Date(holiday.date), event.startDate.toJSDate())); // remove if already exists
const holiday = eventToHoliday(event);
holidays.push(holiday);
});
render();
}, false);
reader.readAsText(input.files[i], "UTF-8");
}
}
function eventToHoliday(event) {
const date = event.startDate.toJSDate();
const holiday = {
date: formatDate(date),
name: event.summary,
type: 'h',
};
return holiday;
}
function formatDate(d) {
return d.getFullYear() + "-" + (d.getMonth() + 1).toString().padStart(2, '0') + "-" + d.getDate().toString().padStart(2, '0');
}
function upload() {
Util.showModal("Saving...");
Util.writeStorage("calendar.days.json", JSON.stringify(holidays), () => {
location.reload(); // reload so we see current data
});
}
function renderHoliday(holiday) {
const localDate = new Date(holiday.date);
const tr = document.createElement('tr');
tr.classList.add('event-row');
const tdTime = document.createElement('td');
tr.appendChild(tdTime);
const inputTime = document.createElement('input');
inputTime.type = "date";
inputTime.classList.add('event-date');
inputTime.classList.add('form-input');
inputTime.value = formatDate(localDate)
inputTime.onchange = (e => {
const date = new Date(inputTime.value);
holiday.date = formatDate(date);
});
tdTime.appendChild(inputTime);
const tdSummary = document.createElement('td');
tr.appendChild(tdSummary);
const inputSummary = document.createElement('input');
inputSummary.type = "text";
inputSummary.classList.add('event-summary');
inputSummary.classList.add('form-input');
inputSummary.maxLength=40;
const summary = (holiday.name?.substring(0, inputSummary.maxLength) || "");
inputSummary.value = summary;
inputSummary.onchange = (e => {
holiday.name = inputSummary.value;
});
tdSummary.appendChild(inputSummary);
inputSummary.onchange();
const tdType = document.createElement('td');
tr.appendChild(tdType);
const selectType = document.createElement("select");
selectType.classList.add('form-select');
tdType.prepend(selectType);
const optionHoliday = document.createElement("option");
optionHoliday.text = "Holiday";
optionHoliday.value = "h";
optionHoliday.selected = holiday.type === "h";
selectType.add(optionHoliday);
const optionOther = document.createElement("option");
optionOther.text = "Other";
optionOther.value = "o";
optionOther.selected = holiday.type === "o";
selectType.add(optionOther);
selectType.onchange = (e => {
holiday.type = e.target.value;
});
const tdRepeat = document.createElement('td');
tr.appendChild(tdRepeat);
const selectRepeat = document.createElement("select");
selectRepeat.classList.add('form-select');
tdRepeat.prepend(selectRepeat);
const optionNever = document.createElement("option");
optionNever.text = "Never";
optionNever.selected = !holiday.repeat;
selectRepeat.add(optionNever);
const optionYearly = document.createElement("option");
optionYearly.text = "Yearly";
optionYearly.value = "y";
optionYearly.selected = holiday.repeat === "y";
selectRepeat.add(optionYearly);
selectRepeat.onchange = (e => {
holiday.repeat = e.target.value;
});
const tdAction = document.createElement('td');
tr.appendChild(tdAction);
const buttonDelete = document.createElement('button');
buttonDelete.classList.add('btn');
buttonDelete.classList.add('btn-action');
tdAction.prepend(buttonDelete);
const iconDelete = document.createElement('i');
iconDelete.classList.add('icon');
iconDelete.classList.add('icon-delete');
buttonDelete.appendChild(iconDelete);
buttonDelete.onclick = (e => {
holidays = holidays.filter(a => a !== holiday);
document.getElementById('events').removeChild(tr);
});
document.getElementById('events').appendChild(tr);
document.getElementById('upload').disabled = false;
}
function addHoliday() {
const holiday = {date: formatDate(new Date()), type: 'h'};
renderHoliday(holiday);
holidays.push(holiday);
render();
}
function getData() {
Util.showModal("Loading...");
Puck.write(`\x10(function() {
Bluetooth.print(JSON.stringify(require("Storage").list("calendar.days.json").sort()));
})()\n`, contents => {
const fileNames = JSON.parse(contents);
if (fileNames.length > 0) {
Util.readStorage('calendar.days.json',data=>{
holidays = JSON.parse(data || "[]") || [];
Util.hideModal();
render();
});
} else {
holidays = [];
Util.hideModal();
}
});
}
// Called when app starts
function onInit() {
getData();
}
</script>
</head>
<body>
<h4 class="float-left">Holidays</h4>
<div class="float-right">
<button class="btn" onclick="addHoliday();">
<i class="icon icon-plus"></i>
</button>
</div>
<table class="table table-scroll" style="clear:both;">
<thead>
<tr>
<th>Date</th>
<th>Holiday</th>
<th>Type</th>
<th>Repeat</th>
<th></th>
</tr>
</thead>
<tbody id="events">
</tbody>
</table>
<div class="divider"></div>
<div class="form-horizontal">
<div class="form-group">
<div class="col-5 col-xs-12">
<label class="form-label" for="fileinput">Add from iCalendar file</label>
</div>
<div class="col-7 col-xs-12">
<input id="fileinput" class="form-input" type="file" onchange="readFile(this)" accept=".ics,.ifb,.ical,.ifbf" multiple/>
</div>
</div>
</div>
<div class="divider"></div>
<button id="upload" class="btn btn-primary" onClick="upload()" disabled>Upload</button>
<button id="reload" class="btn" onClick="location.reload()">Reload</button>
</body>
</html>

View File

@ -1,7 +1,7 @@
{ {
"id": "calendar", "id": "calendar",
"name": "Calendar", "name": "Calendar",
"version": "0.13", "version": "0.14",
"description": "Simple calendar", "description": "Simple calendar",
"icon": "calendar.png", "icon": "calendar.png",
"screenshots": [{"url":"screenshot_calendar.png"}], "screenshots": [{"url":"screenshot_calendar.png"}],
@ -9,10 +9,11 @@
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"allow_emulator": true, "allow_emulator": true,
"interface": "interface.html",
"storage": [ "storage": [
{"name":"calendar.app.js","url":"calendar.js"}, {"name":"calendar.app.js","url":"calendar.js"},
{"name":"calendar.settings.js","url":"settings.js"}, {"name":"calendar.settings.js","url":"settings.js"},
{"name":"calendar.img","url":"calendar-icon.js","evaluate":true} {"name":"calendar.img","url":"calendar-icon.js","evaluate":true}
], ],
"data": [{"name":"calendar.json"}] "data": [{"name":"calendar.json"}, {"name":"calendar.days.json"}]
} }

View File

@ -9,4 +9,4 @@
0.07: Convert Yes/No On/Off in settings to checkboxes 0.07: Convert Yes/No On/Off in settings to checkboxes
0.08: Fix the wrapping of intervals/timeouts with parameters 0.08: Fix the wrapping of intervals/timeouts with parameters
Fix the widget drawing if widgets are hidden and Bangle.setLCDBrightness is called Fix the widget drawing if widgets are hidden and Bangle.setLCDBrightness is called
0.09: Cache the app-launch info 0.09: Accidental version bump

View File

@ -1 +1,2 @@
0.01: Initial release 0.01: Initial release
0.02: Cache the app-launch info

View File

@ -2,7 +2,7 @@
"id": "ratchet_launch", "id": "ratchet_launch",
"name": "Ratchet Launcher", "name": "Ratchet Launcher",
"shortName": "Ratchet", "shortName": "Ratchet",
"version": "0.01", "version": "0.02",
"description": "Launcher with discrete scrolling for quicker app selection", "description": "Launcher with discrete scrolling for quicker app selection",
"icon": "app.png", "icon": "app.png",
"type": "launch", "type": "launch",

View File

@ -3,7 +3,7 @@
<link rel="stylesheet" href="../../css/spectre.min.css"> <link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css"> <link rel="stylesheet" href="../../css/spectre-icons.min.css">
<script src="../../core/lib/interface.js"></script> <script src="../../core/lib/interface.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ical.js/0.0.3/ical.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ical.js/1.5.0/ical.min.js"></script>
<script> <script>
let dataElement = document.getElementById("data"); let dataElement = document.getElementById("data");
let alarms; let alarms;
@ -17,7 +17,7 @@ function readFile(input) {
const reader = new FileReader(); const reader = new FileReader();
reader.addEventListener("load", () => { reader.addEventListener("load", () => {
const jCalData = ICAL.parse(reader.result); const jCalData = ICAL.parse(reader.result);
const comp = new ICAL.Component(jCalData[1]); const comp = new ICAL.Component(jCalData);
// Fetch the VEVENT part // Fetch the VEVENT part
comp.getAllSubcomponents('vevent').forEach(vevent => { comp.getAllSubcomponents('vevent').forEach(vevent => {
event = new ICAL.Event(vevent); event = new ICAL.Event(vevent);
@ -50,13 +50,17 @@ function dateFromAlarm(alarm) {
return new Date(date.getTime() + alarm.t); return new Date(date.getTime() + alarm.t);
} }
function formatDate(d) {
return d.getFullYear() + "-" + (d.getMonth() + 1).toString().padStart(2, '0') + "-" + d.getDate().toString().padStart(2, '0');
}
function getAlarmDefaults() { function getAlarmDefaults() {
const date = new Date(); const date = new Date();
return { return {
on: true, on: true,
t: dateToMsSinceMidnight(date), t: dateToMsSinceMidnight(date),
dow: 127, dow: 127,
date: date.toISOString().substring(0,10), date: formatDate(date),
last: 0, last: 0,
rp: "defaultRepeat" in schedSettings ? schedSettings.defaultRepeat : false, rp: "defaultRepeat" in schedSettings ? schedSettings.defaultRepeat : false,
vibrate: "defaultAlarmPattern" in schedSettings ? schedSettings.defaultAlarmPattern : "::", vibrate: "defaultAlarmPattern" in schedSettings ? schedSettings.defaultAlarmPattern : "::",
@ -72,7 +76,7 @@ function eventToAlarm(event, offsetMs) {
id: event.uid, id: event.uid,
msg: event.summary, msg: event.summary,
t: dateToMsSinceMidnight(date), t: dateToMsSinceMidnight(date),
date: date.toISOString().substring(0,10), date: formatDate(date),
data: {end: event.endDate.toJSDate().toISOString()} data: {end: event.endDate.toJSDate().toISOString()}
}}; }};
if (offsetMs) { // Alarm time is not real event time, so do a backup if (offsetMs) { // Alarm time is not real event time, so do a backup
@ -105,7 +109,7 @@ function renderAlarm(alarm, exists) {
inputTime.onchange = (e => { inputTime.onchange = (e => {
const date = new Date(inputTime.value); const date = new Date(inputTime.value);
alarm.t = dateToMsSinceMidnight(date); alarm.t = dateToMsSinceMidnight(date);
alarm.date = date.toISOString().substring(0,10); alarm.date = formatDate(date);
}); });
tdTime.appendChild(inputTime); tdTime.appendChild(inputTime);

View File

@ -1,3 +1,4 @@
0.01: New app! 0.01: New app!
0.02: Bug fixes 0.02: Bug fixes
0.03: Submitted to the app loader 0.03: Submitted to the app loader
0.04: Reduce battery consumption when the user's not looking

View File

@ -8,7 +8,7 @@ const BUTTON_ICONS = {
reset: heatshrink.decompress(atob("jEYwMA/4BB/+BAQPDAQPnAQIAKv///0///8j///EP//wAQQICBwQUCEhgyCHAQ+CIgI=")) reset: heatshrink.decompress(atob("jEYwMA/4BB/+BAQPDAQPnAQIAKv///0///8j///EP//wAQQICBwQUCEhgyCHAQ+CIgI="))
}; };
let state = storage.readJSON(STATE_PATH); let state = storage.readJSON(STATE_PATH, 1);
const STATE_DEFAULT = { const STATE_DEFAULT = {
wasRunning: false, //If the stopwatch was ever running since being reset wasRunning: false, //If the stopwatch was ever running since being reset
sessionStart: 0, //When the stopwatch was first started sessionStart: 0, //When the stopwatch was first started
@ -157,7 +157,7 @@ function firstTimeStart(now, time) {
elapsedTime: 0, elapsedTime: 0,
}; };
lapFile = 'stlap-' + state.sessionStart + '.json'; lapFile = 'stlap-' + state.sessionStart + '.json';
setupTimerInterval(); setupTimerIntervalFast();
Bangle.buzz(200); Bangle.buzz(200);
drawButtons(); drawButtons();
} }
@ -201,13 +201,15 @@ function start(now, time) {
state.elapsedTime += (state.pausedTime - state.startTime); state.elapsedTime += (state.pausedTime - state.startTime);
state.startTime = now; state.startTime = now;
state.running = true; state.running = true;
setupTimerInterval(); setupTimerIntervalFast();
Bangle.buzz(200); Bangle.buzz(200);
drawTime(); drawTime();
drawButtons(); drawButtons();
} }
Bangle.on("touch", (button, xy) => { Bangle.on("touch", (button, xy) => {
setupTimerIntervalFast();
//In gesture mode, just turn on the light and then return //In gesture mode, just turn on the light and then return
if (gestureMode) { if (gestureMode) {
Bangle.setLCDPower(true); Bangle.setLCDPower(true);
@ -242,6 +244,8 @@ Bangle.on("touch", (button, xy) => {
}); });
Bangle.on('swipe', direction => { Bangle.on('swipe', direction => {
setupTimerIntervalFast();
let now = (new Date()).getTime(); let now = (new Date()).getTime();
let time = getTime(); let time = getTime();
@ -272,12 +276,23 @@ setWatch(() => {
}, BTN1, { repeat: true }); }, BTN1, { repeat: true });
let timerInterval; let timerInterval;
let userWatching = false;
function setupTimerIntervalFast() {
userWatching = true;
setupTimerInterval();
setTimeout(() => {
userWatching = false;
setupTimerInterval();
}, 5000);
}
function setupTimerInterval() { function setupTimerInterval() {
if (timerInterval !== undefined) { if (timerInterval !== undefined) {
clearInterval(timerInterval); clearInterval(timerInterval);
} }
timerInterval = setInterval(drawTime, 10); timerInterval = setInterval(drawTime, userWatching ? 10 : 1000);
} }
function stopTimerInterval() { function stopTimerInterval() {
@ -289,7 +304,7 @@ function stopTimerInterval() {
drawTime(); drawTime();
if (state.running) { if (state.running) {
setupTimerInterval(); setupTimerIntervalFast();
} }
//Save our state when the app is closed //Save our state when the app is closed
@ -300,5 +315,8 @@ E.on('kill', () => {
} }
}); });
// change interval depending of whether the user's looking
Bangle.on("twist", setupTimerIntervalFast);
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();

View File

@ -1,7 +1,7 @@
{ {
"id": "stlap", "id": "stlap",
"name": "Stopwatch", "name": "Stopwatch",
"version": "0.03", "version": "0.04",
"description": "A stopwatch that remembers its state, with a lap timer and a gesture mode (enable by swiping)", "description": "A stopwatch that remembers its state, with a lap timer and a gesture mode (enable by swiping)",
"icon": "icon.png", "icon": "icon.png",
"type": "app", "type": "app",
@ -20,5 +20,9 @@
"url": "icon.js", "url": "icon.js",
"evaluate": true "evaluate": true
} }
],
"data": [
{"name":"stlap.state.json"},
{"wildcard":"stlap-*.json"}
] ]
} }

View File

@ -1 +1,2 @@
0.01: Initial release 0.01: Initial release
0.02: Disallow emulator

View File

@ -1,7 +1,7 @@
{ {
"id": "widtemp", "id": "widtemp",
"name": "Temperature widget", "name": "Temperature widget",
"version": "0.01", "version": "0.02",
"description": "A temperature widget, showing the current internal temperature", "description": "A temperature widget, showing the current internal temperature",
"readme": "README.md", "readme": "README.md",
"icon": "widtemp.png", "icon": "widtemp.png",
@ -10,7 +10,7 @@
"tags": "widget,health", "tags": "widget,health",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : {}, "dependencies" : {},
"allow_emulator":true, "allow_emulator":false,
"storage": [ "storage": [
{"name":"widtemp.wid.js","url":"widtemp.wid.js"} {"name":"widtemp.wid.js","url":"widtemp.wid.js"}
] ]