changed agenda app name names to agenda.*
parent
e606f85d45
commit
95a931b071
|
|
@ -0,0 +1,175 @@
|
||||||
|
/* CALENDAR is a list of:
|
||||||
|
{id:int,
|
||||||
|
type,
|
||||||
|
timestamp,
|
||||||
|
durationInSeconds,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
location,
|
||||||
|
color:int,
|
||||||
|
calName,
|
||||||
|
allDay: bool,
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
var FILE = "android.calendar.json";
|
||||||
|
|
||||||
|
var Locale = require("locale");
|
||||||
|
|
||||||
|
var fontSmall = "6x8";
|
||||||
|
var fontMedium = g.getFonts().includes("12x20")?"12x20":"6x8:2";
|
||||||
|
var fontBig = g.getFonts().includes("12x20")?"12x20":"6x8:2";
|
||||||
|
var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
|
||||||
|
|
||||||
|
//FIXME maybe write the end from GB already? Not durationInSeconds here (or do while receiving?)
|
||||||
|
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||||
|
var settings = require("Storage").readJSON("agenda.settings.json",true)||{};
|
||||||
|
|
||||||
|
CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp);
|
||||||
|
|
||||||
|
function getDate(timestamp) {
|
||||||
|
return new Date(timestamp*1000);
|
||||||
|
}
|
||||||
|
function formatDay(date) {
|
||||||
|
let formattedDate = Locale.dow(date, 1) + " " + Locale.date(date).replace(/\d\d\d\d/,"");
|
||||||
|
if (!settings.useToday) return formattedDate;
|
||||||
|
|
||||||
|
const today = new Date(Date.now());
|
||||||
|
const delta = deltaDate(today, date);
|
||||||
|
|
||||||
|
if (delta === 0) return /*LANG*/"Today ";
|
||||||
|
else if (delta === 1) return /*LANG*/"Tomorrow ";
|
||||||
|
else if (delta <= 5) return require("locale").dow(date);
|
||||||
|
else return formattedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateLong(date, includeDay, allDay) {
|
||||||
|
let shortTime = Locale.time(date,1)+Locale.meridian(date);
|
||||||
|
if(allDay) shortTime = "";
|
||||||
|
if(includeDay || allDay) {
|
||||||
|
return formatDay(date)+" "+shortTime;
|
||||||
|
}
|
||||||
|
return shortTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateShort(date, allDay) {
|
||||||
|
return formatDay(date)+(allDay?"":Locale.time(date,1)+Locale.meridian(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
function deltaDate(date1, date2) {
|
||||||
|
let tzo = date1.getTimezoneOffset() * 60000; // time zone offset in minutes * 60000 ms/min = tzo in ms
|
||||||
|
return (Math.floor((date2.valueOf() - tzo) / 86400000) - Math.floor((date1.valueOf() - tzo) / 86400000));
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = [];
|
||||||
|
function showEvent(ev) {
|
||||||
|
var bodyFont = fontBig;
|
||||||
|
if(!ev) return;
|
||||||
|
g.setFont(bodyFont);
|
||||||
|
//var lines = [];
|
||||||
|
if (ev.title) lines = g.wrapString(ev.title, g.getWidth()-10);
|
||||||
|
var titleCnt = lines.length;
|
||||||
|
var start = getDate(ev.timestamp);
|
||||||
|
var end = getDate((+ev.timestamp) + (+ev.durationInSeconds));
|
||||||
|
var includeDay = true;
|
||||||
|
if (titleCnt) lines.push(""); // add blank line after title
|
||||||
|
if(start.getDay() == end.getDay() && start.getMonth() == end.getMonth())
|
||||||
|
includeDay = false;
|
||||||
|
if(includeDay && ev.allDay) {
|
||||||
|
//single day all day (average to avoid getting previous day)
|
||||||
|
lines = lines.concat(
|
||||||
|
g.wrapString(formatDateLong(new Date((start+end)/2), includeDay, ev.allDay), g.getWidth()-10));
|
||||||
|
} else if(includeDay || ev.allDay) {
|
||||||
|
lines = lines.concat(
|
||||||
|
/*LANG*/"Start"+":",
|
||||||
|
g.wrapString(formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10),
|
||||||
|
/*LANG*/"End"+":",
|
||||||
|
g.wrapString(formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10));
|
||||||
|
} else {
|
||||||
|
lines = lines.concat(
|
||||||
|
g.wrapString(formatDateShort(start,true), g.getWidth()-10),
|
||||||
|
g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10),
|
||||||
|
g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10));
|
||||||
|
}
|
||||||
|
if(ev.location)
|
||||||
|
lines = lines.concat("",/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10));
|
||||||
|
if(ev.description && ev.description.trim())
|
||||||
|
lines = lines.concat("",g.wrapString(ev.description, g.getWidth()-10));
|
||||||
|
if(ev.calName)
|
||||||
|
lines = lines.concat("",/*LANG*/"Calendar"+": ", g.wrapString(ev.calName, g.getWidth()-10));
|
||||||
|
lines = lines.concat("",/*LANG*/"< Back");
|
||||||
|
E.showScroller({
|
||||||
|
h : g.getFontHeight(), // height of each menu item in pixels
|
||||||
|
c : lines.length, // number of menu items
|
||||||
|
// a function to draw a menu item
|
||||||
|
draw : function(idx, r) {
|
||||||
|
// FIXME: in 2v13 onwards, clearRect(r) will work fine. There's a bug in 2v12
|
||||||
|
g.setBgColor(idx<titleCnt ? g.theme.bg2 : g.theme.bg).
|
||||||
|
setColor(idx<titleCnt ? g.theme.fg2 : g.theme.fg).
|
||||||
|
clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
||||||
|
g.setFont(bodyFont).drawString(lines[idx], r.x, r.y);
|
||||||
|
}, select : function(idx) {
|
||||||
|
if (idx>=lines.length-2)
|
||||||
|
showList();
|
||||||
|
},
|
||||||
|
back : () => showList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showList() {
|
||||||
|
//it might take time for GB to delete old events, decide whether to show them grayed out or hide entirely
|
||||||
|
if(!settings.pastEvents) {
|
||||||
|
let now = new Date();
|
||||||
|
//TODO add threshold here?
|
||||||
|
CALENDAR = CALENDAR.filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000);
|
||||||
|
}
|
||||||
|
if(CALENDAR.length == 0) {
|
||||||
|
E.showMessage(/*LANG*/"No events");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
E.showScroller({
|
||||||
|
h : 74,
|
||||||
|
c : CALENDAR.length, //.max(CALENDAR.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
|
||||||
|
draw : function(idx, r) {"ram"
|
||||||
|
var ev = CALENDAR[idx];
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.clearRect(r.x, r.y, r.x+r.w, r.y+r.h);
|
||||||
|
if (!ev) return;
|
||||||
|
let isPast = false;
|
||||||
|
var y = r.y + 5;
|
||||||
|
let title = ev.title;
|
||||||
|
let date = formatDateShort(getDate(ev.timestamp),ev.allDay);
|
||||||
|
// if (ev.location) var location = ev.location; // I don't think it's neccesary to show locaton in list view
|
||||||
|
if(settings.pastEvents) isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
|
||||||
|
if (date) {
|
||||||
|
let fh = g.setFont(fontMedium).getFontHeight();
|
||||||
|
//var col = g.blendColor(g.theme.fg,g.theme.bg, 0.75);
|
||||||
|
let col = g.theme.bg2;
|
||||||
|
g.setBgColor(col).clearRect(r.x, y, r.x + r.w, y + fh);
|
||||||
|
g.setBgColor(g.theme.bg);
|
||||||
|
g.setFontAlign(-1,-1).setColor(isPast ? "#888" : g.theme.fg);
|
||||||
|
g.drawString(date, r.x, y);
|
||||||
|
y += fh + 5; // set new y to position below date
|
||||||
|
}
|
||||||
|
if (title) {
|
||||||
|
let str = g.wrapString(title, r.x + r.w);
|
||||||
|
let numLines = str.length;
|
||||||
|
let titleStr = numLines > 1 ? str[0] + "\n" + str[1] : str[0];
|
||||||
|
if(ev.color) {
|
||||||
|
g.setColor("#"+(0x1000000+Number(ev.color)).toString(16).padStart(6,"0"));
|
||||||
|
g.fillRect(r.x, y, r.x+3, r.y+r.h-3); // color bar
|
||||||
|
}
|
||||||
|
y = numLines > 1 ? y : y+5;
|
||||||
|
g.setFontAlign(-1,-1).setFont(fontBig)
|
||||||
|
.setColor(isPast ? "#888" : g.theme.fg).drawString(titleStr, r.x + 5, y);
|
||||||
|
}
|
||||||
|
g.setColor("#888").fillRect(r.x, r.y+r.h-1, r.x+r.w-1, r.y+r.h-1); // dividing line between items
|
||||||
|
},
|
||||||
|
select : idx => showEvent(CALENDAR[idx]),
|
||||||
|
back : () => load()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
showList();
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
(function(back) {
|
||||||
|
function gbSend(message) {
|
||||||
|
Bluetooth.println("");
|
||||||
|
Bluetooth.println(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
var settings = require("Storage").readJSON("agenda.settings.json",1)||{};
|
||||||
|
function updateSettings() {
|
||||||
|
require("Storage").writeJSON("agenda.settings.json", settings);
|
||||||
|
}
|
||||||
|
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||||
|
var mainmenu = {
|
||||||
|
"" : { "title" : "Agenda" },
|
||||||
|
"< Back" : back,
|
||||||
|
/*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?/*LANG*/"Yes":/*LANG*/"No" },
|
||||||
|
/*LANG*/"Force calendar sync" : () => {
|
||||||
|
if(NRF.getSecurityStatus().connected) {
|
||||||
|
E.showPrompt(/*LANG*/"Do you want to also clear the internal database first?", {
|
||||||
|
buttons: {/*LANG*/"Yes": 1, /*LANG*/"No": 2, /*LANG*/"Cancel": 3}
|
||||||
|
}).then((v)=>{
|
||||||
|
switch(v) {
|
||||||
|
case 1:
|
||||||
|
require("Storage").writeJSON("android.calendar.json",[]);
|
||||||
|
CALENDAR = [];
|
||||||
|
/* falls through */
|
||||||
|
case 2:
|
||||||
|
gbSend({t:"force_calendar_sync", ids: CALENDAR.map(e=>e.id)});
|
||||||
|
E.showAlert(/*LANG*/"Request sent to the phone").then(()=>E.showMenu(mainmenu));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
E.showMenu(mainmenu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
E.showAlert(/*LANG*/"You are not connected").then(()=>E.showMenu(mainmenu));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*LANG*/"Show past events" : {
|
||||||
|
value : !!settings.pastEvents,
|
||||||
|
onchange: v => {
|
||||||
|
settings.pastEvents = v;
|
||||||
|
updateSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*LANG*/"Use 'Today',..." : {
|
||||||
|
value : !!settings.useToday,
|
||||||
|
onchange: v => {
|
||||||
|
settings.useToday = v;
|
||||||
|
updateSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
E.showMenu(mainmenu);
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue