1
0
Fork 0

Merge remote-tracking branch 'upstream/master'

master
hughbarney 2021-04-28 22:47:09 +01:00
commit 40851ec0c4
17 changed files with 569 additions and 247 deletions

View File

@ -219,8 +219,8 @@
{ "id": "slidingtext",
"name": "Sliding Clock",
"icon": "slidingtext.png",
"version":"0.02",
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported",
"version":"0.05",
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently English, French, Japanese, Spanish and German are supported",
"tags": "clock",
"type":"clock",
"allow_emulator":true,
@ -232,8 +232,10 @@
{"name":"slidingtext.locale.en.js","url":"slidingtext.locale.en.js"},
{"name":"slidingtext.locale.en2.js","url":"slidingtext.locale.en2.js"},
{"name":"slidingtext.utils.en.js","url":"slidingtext.utils.en.js"},
{"name":"slidingtext.locale.es.js","url":"slidingtext.locale.es.js"},
{"name":"slidingtext.locale.fr.js","url":"slidingtext.locale.fr.js"},
{"name":"slidingtext.locale.jp.js","url":"slidingtext.locale.jp.js"},
{"name":"slidingtext.locale.de.js","url":"slidingtext.locale.de.js"},
{"name":"slidingtext.dtfmt.js","url":"slidingtext.dtfmt.js"}
]
},
@ -459,7 +461,7 @@
{ "id": "heart",
"name": "Heart Rate Recorder",
"icon": "app.png",
"version":"0.04",
"version":"0.05",
"interface": "interface.html",
"description": "Application that allows you to record your heart rate. Can run in background",
"tags": "tool,health,widget",
@ -3042,7 +3044,7 @@
"name": "Gadgetbridge Music Controls",
"shortName":"Music Controls",
"icon": "icon.png",
"version":"0.02",
"version":"0.03",
"description": "Control the music on your Gadgetbridge-connected phone",
"tags": "tools,bluetooth,gadgetbridge,music",
"type":"app",

View File

@ -1,2 +1,3 @@
0.01: Initial version
0.02: Increase text brightness, improve controls, (try to) reduce memory usage
0.02: Increase text brightness, improve controls, (try to) reduce memory usage
0.03: Only auto-start if active app is a clock, auto close after 1 hour of inactivity

View File

@ -11,7 +11,8 @@ let info = {
n: 0,
c: 0,
};
const TOUT = 300000; // auto close timeout: 5 minutes (in ms)
const POUT = 300000; // auto close timeout when paused: 5 minutes (in ms)
const IOUT = 3600000; // auto close timeout for inactivity: 1 hour (in ms)
///////////////////////
// Self-repeating timeouts
@ -44,7 +45,7 @@ function brightness() {
if (!fade) {
return 1;
}
return Math.max(0, 1-((Date.now()-fade)/TOUT));
return Math.max(0, 1-((Date.now()-fade)/POUT));
}
// Scroll long track names
@ -396,26 +397,50 @@ function musicInfo(e) {
if (Bangle.isLCDOn()) {
drawMusic();
}
if (tIxt) {
clearTimeout(tIxt);
tIxt = null;
}
if (auto && stat==="play") {
// if inactive for double song duration (or an hour if unknown), load the clock
// i.e. phone finished playing without bothering to notify the watch
tIxt = setTimeout(load, (info.dur*2000) || IOUT);
}
}
let tXit;
let tPxt, tIxt;
function musicState(e) {
stat = e.state;
// if paused for five minutes, load the clock
// (but timeout resets if we get new info, even while paused)
if (tXit) {
clearTimeout(tXit);
if (tPxt) {
clearTimeout(tPxt);
tPxt = null;
}
if (tIxt) {
clearTimeout(tIxt);
tIxt = null;
}
tXit = null;
fade = null;
delete info.track_color;
if (stat!=="play" && auto) {
if (stat==="stop") { // never actually happens with my phone :-(
load();
} else { // also quit when paused for a long time
tXit = setTimeout(load, TOUT);
fade = Date.now();
fadeOut();
if (auto) { // auto opened -> auto close
switch(stat) {
case "stop": // never actually happens with my phone :-(
load();
break;
case "play":
// if inactive for double song duration (or an hour if unknown), load the clock
// i.e. phone finished playing without bothering to notify the watch
tIxt = setTimeout(load, (info.dur*2000) || IOUT);
break;
case "pause":
default:
// quit when paused for a long time
// also fade out track info while waiting for this
tPxt = setTimeout(load, POUT);
fade = Date.now();
fadeOut();
break;
}
}
if (Bangle.isLCDOn()) {
@ -538,7 +563,7 @@ function startLCDWatch() {
/////////////////////
// check for saved music stat (by widget) to load
g.clear();
global.gbmusic_active = true; // we don't need our widget
global.gbmusic_active = true; // we don't need our widget (needed for <2.09 devices)
Bangle.loadWidgets();
Bangle.drawWidgets();
delete (global.gbmusic_active);

View File

@ -1,38 +1,44 @@
(() => {
if (global.gbmusic_active || !(require("Storage").readJSON("gbmusic.json", 1) || {}).autoStart) {
return
return;
}
if (typeof __FILE__ === 'string') { // only exists since 2v09
const info = require("Storage").readJSON(__FILE__.split(".")[0]+".info", 1) || false;
if (info && info.type!=="clock") { // info can have no type (but then it isn't a clock)
return;
}
}
let state, info
let state, info;
function checkMusic() {
if (state!=="play" || !info) {
return
return;
}
// playing music: launch music app
require("Storage").writeJSON("gbmusic.load.json", {
state: state,
info: info,
})
load("gbmusic.app.js")
});
load("gbmusic.app.js");
}
const _GB = global.GB
const _GB = global.GB;
global.GB = (event) => {
// we eat music events!
switch(event.t) {
case "musicinfo":
info = event
delete(info.t)
checkMusic()
break
info = event;
delete (info.t);
checkMusic();
break;
case "musicstate":
state = event.state
checkMusic()
break
state = event.state;
checkMusic();
break;
default:
if (_GB) {
setTimeout(_GB, 0, event)
setTimeout(_GB, 0, event);
}
}
}
})()
};
})();

View File

@ -3,3 +3,11 @@
Clean up recordings on app removal
0.03: added graphing feature of 164 latest measurements
0.04: Fix memory usage when viewing HRM traces
0.05: Add loading screen for viewRecord
List average, minimum & maximum measurement in viewRecord
Disable recording only when current recording file is erased
Fix timezone offset
Draw chart based on height and width of display instead of hard-coded limits
Reduce memory usage by ~30%
Generate scale based on defined minimum and maximum measurement
Added background line on 50% to ease estimation of drawn values

View File

@ -1,25 +1,17 @@
const GraphXZero = 40;
const GraphYZero = 200;
const GraphY100 = 80;
E.setFlags({pretokenise:1});
const GraphMarkerOffset = 5;
const MaxValueCount = 164;
const GraphXMax = GraphXZero + MaxValueCount;
function log(msg) {
console.log("heart: " + msg + "; mem used: " + process.memory().usage / process.memory().blocksize);
return;
}
log("start");
Bangle.loadWidgets();
Bangle.drawWidgets();
var settings = require("Storage").readJSON("heart.json",1)||{};
var globalSettings = require('Storage').readJSON('setting.json', true) || {timezone: 0};
require('DateExt').locale({
str: "0D.0M. 0h:0m",
offset: [
globalSettings.timezone * 60,
globalSettings.timezone * 60
]
});
function getFileNbr(n) {
return ".heart"+n.toString(36);
}
@ -28,6 +20,7 @@ function updateSettings() {
require("Storage").write("heart.json", settings);
if (WIDGETS["heart"])
WIDGETS["heart"].reload();
return;
}
function showMainMenu() {
@ -52,214 +45,232 @@ function showMainMenu() {
updateSettings();
}
},
'View Records': ()=>{viewRecords()},
'Graph Records': ()=>{graphRecords()},
'View Records': ()=>{createRecordMenu(viewRecord.bind());},
'Graph Records': ()=>{createRecordMenu(graphRecord.bind());},
'< Back': ()=>{load();}
};
return E.showMenu(mainMenu);
}
function viewRecords() {
// Date().as().str cannot be used as it always returns UTC time
function getDateString(timestamp) {
var date = new Date(timestamp);
var day = date.getDate() < 10 ? "0" + date.getDate().toString() : date.getDate().toString();
var month = date.getMonth() < 10 ? "0" + date.getMonth().toString() : date.getMonth().toString();
return day + "." + month + "." + date.getFullYear();
}
// Date().as().str cannot be used as it always returns UTC time
function getTimeString(timestamp) {
var date = new Date(timestamp);
var hour = date.getHours() < 10 ? '0' + date.getHours().toString() : date.getHours().toString();
var minute = date.getMinutes() < 10 ? '0' + date.getMinutes().toString() : date.getMinutes().toString();
return hour + ':' + minute;
}
function createRecordMenu(func) {
const menu = {
'': { 'title': 'Heart Records' }
};
var found = false;
for (var n=0;n<36;n++) {
var f = require("Storage").open(getFileNbr(n),"r");
if (f.readLine()!==undefined) {
menu["Record "+n] = viewRecord.bind(null,n);
var line = require("Storage").open(getFileNbr(n),"r").readLine();
if (line!==undefined) {
menu["#" + n + " " + getDateString(line.split(",")[0]*1000) + " " + getTimeString(line.split(",")[0]*1000)] = func.bind(null, n);
found = true;
}
}
if (!found)
menu["No Records Found"] = function(){};
menu['< Back'] = ()=>{showMainMenu()};
menu['< Back'] = ()=>{showMainMenu();};
return E.showMenu(menu);
}
function viewRecord(n) {
E.showMenu({'': 'Heart Record '+n});
E.showMessage(
"Loading Data ...\n\nMay take a while,\nwill vibrate\nwhen done.",
'Heart Record '+n
);
const menu = {
'': { 'title': 'Heart Record '+n }
};
var heartCount = 0;
var heartTime;
var f = require("Storage").open(getFileNbr(n),"r");
var l = f.readLine();
if (l!==undefined) {
var c = l.split(",");
heartTime = new Date(c[0]*1000);
}
// using arrays for memory optimization
var limits = Uint8Array(2);
// using arrays for memory optimization
var avg = Uint32Array(2);
// minimum
limits[0] = 2000;
// maximum
limits[1] = 0;
// count
avg[0] = 0;
// average sum
avg[1] = 0;
var count = 0;
var value = 0;
if (l!==undefined)
heartTime = new Date(l.split(",")[0]*1000);
log("parsing records");
while (l!==undefined) {
heartCount++;
// TODO: min/max/average of heart rate?
count++;
if (parseInt(l.split(',')[2]) >= 70) {
avg[0]++;
value = parseInt(l.split(',')[1]);
if (value < limits[0]) {
limits[0] = value;
} else if (value > limits[1]) {
limits[1] = value;
}
avg[1] += value;
}
l = f.readLine();
}
l = undefined;
value = undefined;
log("finished parsing");
if (heartTime)
menu[" "+heartTime.toString().substr(4,17)] = function(){};
menu[heartCount+" records"] = function(){};
// TODO: option to draw it? Just scan through, project using min/max
menu['Erase'] = function() {
menu[count + " records"] = function(){};
menu["Min: " + limits[0]] = function(){};
menu["Max: " + limits[1]] = function(){};
menu["Avg: " + Math.round(avg[1] / avg[0])] = function(){};
menu["Erase"] = function() {
E.showPrompt("Delete Record?").then(function(v) {
if (v) {
settings.isRecording = false;
updateSettings();
var f = require("Storage").open(getFileNbr(n),"r");
f.erase();
viewRecords();
if (n == settings.fileNbr) {
settings.isRecording = false;
updateSettings();
}
require("Storage").open(getFileNbr(n),"r").erase();
E.showMenu();
createRecordMenu(viewRecord.bind());
} else
viewRecord(n);
return viewRecord(n);
});
};
menu['< Back'] = ()=>{viewRecords()};
print(menu);
menu['< Back'] = ()=>{createRecordMenu(viewRecord.bind());};
Bangle.buzz(200, 0.3);
return E.showMenu(menu);
}
function graphRecords() {
const menu = {
'': { 'title': 'Heart Records' }
};
var found = false;
for (var n=0;n<36;n++) {
var f = require("Storage").open(getFileNbr(n),"r");
var line = f.readLine();
if (line!==undefined) {
menu["#"+n+" "+Date(line.split(",")[0]*1000).as().str] = graphRecord.bind(null,n);
found = true;
}
}
if (!found)
menu["No Records Found"] = function(){};
menu['< Back'] = ()=>{showMainMenu()};
return E.showMenu(menu);
}
// based on batchart
function renderHomeIcon() {
//Home for Btn2
g.setColor(1, 1, 1);
g.drawLine(220, 118, 227, 110);
g.drawLine(227, 110, 234, 118);
g.drawPoly([222,117,222,125,232,125,232,117], false);
g.drawRect(226,120,229,125);
}
function renderChart() {
// Left Y axis (Battery)
g.setColor(1, 1, 0);
g.drawLine(GraphXZero, GraphYZero + GraphMarkerOffset, GraphXZero, GraphY100);
g.setFontAlign(1, -1, 0);
g.drawString("150", 35, GraphY100 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, GraphY100, GraphXZero, GraphY100);
g.drawString("125", 35, GraphYZero - 110 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("100", 35, GraphYZero - 100 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("90", 35, GraphYZero - 90 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("80", 35, GraphYZero - 70 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("70", 35, GraphYZero - 50 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("60", 35, GraphYZero - 30 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("50", 35, GraphYZero - 20 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("40", 35, GraphYZero - 10 - GraphMarkerOffset);
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
g.drawString("30", 35, GraphYZero - GraphMarkerOffset);
g.setColor(1, 1, 1);
g.drawLine(GraphXZero - GraphMarkerOffset, GraphYZero, GraphXMax + GraphMarkerOffset, GraphYZero);
console.log("Finished drawing chart");
}
// as drawing starts at 30 HRM decreasing measrure by 30
// recalculate for range 110-150 as only 20 pixels are available
function getY(measure) {
positionY = GraphYZero - measure + 30;
if (100 < measure < 150) {
positionY = GraphYZero - ( 100 + Math.round((measure - 100)/2) ) + 30;
g.setColor(1, 0, 0);
} else if (60 < measrure < 100) {
positionY = GraphYZero - ( 30 + Math.round((measure - 30)/2) ) + 30;
g.setColor(0, 1, 0);
}
if (positionY > GraphYZero) {
positionY = GraphYZero;
g.setColor(1, 0, 0);
}
if (positionY < GraphY100) {
positionY = GraphY100;
g.setColor(1, 0, 0);
}
return positionY;
}
function stop() {
E.showMenu();
load();
}
function graphRecord(n) {
E.showMenu({'': 'Heart Record '+n});
var headline = "Heart Record " + n;
E.showMenu({'': headline});
E.showMessage(
"Loading Data ...\n\nMay take a while,\nwill vibrate\nwhen done.",
'Heart Record '+n
headline
);
g.setFont("Vector", 10);
var lastPixel;
const MinMeasurement = 30;
const MaxMeasurement = 150;
const GraphXLabel = 35;
const GraphXZero = 40;
const GraphY100 = 60;
const GraphMarkerOffset = 5;
// calculate number of pixels based on display width
const MaxValueCount = g.getWidth() - GraphXZero - ( g.getWidth() - 220 ) - GraphMarkerOffset;
// calculate Y axis "0" pixel
const GraphYZero = g.getHeight() - g.setFont("Vector", 10).getFontHeight() - GraphMarkerOffset * 2;
// calculate X axis max drawable pixel
const GraphXMax = GraphXZero + MaxValueCount;
// calculate space between labels of scale
const LabelOffset = (GraphYZero - GraphY100) / (MaxMeasurement - MinMeasurement);
var lineCount = 0;
var positionX = GraphXZero;
var positionY = GraphYZero;
var startLine = 1;
var tempCount = 0;
var f = require("Storage").open(getFileNbr(n),"r");
var line = f.readLine();
var times = Array(2);
console.log("Counting lines");
log("Counting lines");
while (line !== undefined) {
lineCount++;
line = f.readLine();
}
console.log(`Line count: ${lineCount}`);
if (lineCount > MaxValueCount) {
log(`lineCount: ${lineCount}`);
if (lineCount > MaxValueCount)
startLine = lineCount - MaxValueCount;
}
console.log(`start: ${startLine}`);
f = undefined;
line = undefined;
lineCount = undefined;
log(`startLine: ${startLine}`);
f = require("Storage").open(getFileNbr(n),"r");
line = f.readLine();
var times = Uint32Array(2);
var tempCount = 0;
var positionX = GraphXZero;
var positionY = GraphYZero;
var measure;
while (line !== undefined) {
currentLine = line;
line = f.readLine();
tempCount++;
if (tempCount == startLine) {
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
renderHomeIcon();
renderChart();
// generating rgaph in loop when reaching startLine to keep loading
// message on screen until graph can be drawn
g.clear().
// Home for Btn2
setColor(1, 1, 1).
drawLine(220, 118, 227, 110).
drawLine(227, 110, 234, 118).
drawPoly([222,117,222,125,232,125,232,117], false).
drawRect(226,120,229,125).
// headline
setFontAlign(0, -1, 0).
setFont("6x8", 2).
drawString(headline, g.getWidth()/2 - headline.length/2, GraphY100 - g.getFontHeight() - GraphMarkerOffset).
// Chart
setColor(1, 1, 0).
// horizontal bottom line
drawLine(GraphXZero, GraphYZero + GraphMarkerOffset, GraphXZero, GraphY100).
// vertical left line
drawLine(GraphXZero - GraphMarkerOffset, GraphYZero, GraphXMax + GraphMarkerOffset, GraphYZero).
// scale indicator line for 100%
drawLine(GraphXZero - GraphMarkerOffset, GraphY100, GraphXZero, GraphY100).
// scale indicator line for 50%
drawLine(GraphXZero - GraphMarkerOffset, GraphY100 + (GraphYZero - GraphY100)/2, GraphXZero, GraphY100 + (GraphYZero - GraphY100)/2).
// background line for 50%
setColor(1, 1, 1).
drawLine(GraphXZero + 1, GraphY100 + (GraphYZero - GraphY100)/2, GraphXMax, GraphY100 + (GraphYZero - GraphY100)/2).
setFontAlign(1, -1, 0).
setFont("Vector", 10);
// scale text
for (var i = MaxMeasurement; i >= MinMeasurement; i-=10) {
g.drawString(i, GraphXLabel, GraphY100 + LabelOffset * ( MaxMeasurement - i ) - GraphMarkerOffset);
}
log("Finished drawing chart");
} else if (tempCount > startLine) {
positionX++;
if (parseInt(currentLine.split(",")[2]) >= 70) {
g.setColor(1, 1, 1);
g.setColor(1, 0.3, 0.3);
oldPositionY = positionY;
positionY = getY(parseInt(currentLine.split(",")[1]));
if (times[0] === undefined) {
measure = parseInt(currentLine.split(",")[1]);
positionY = GraphYZero - measure + MinMeasurement;
if (positionY > GraphYZero) {
positionY = GraphYZero;
}
if (positionY < GraphY100) {
positionY = GraphY100;
}
if (times[0] === 0) {
times[0] = parseInt(currentLine.split(",")[0]);
}
if (tempCount == startLine + 1) {
@ -270,26 +281,30 @@ function graphRecord(n) {
}
}
}
g.flip();
}
g.setColor(1, 1, 0);
g.setFont("Vector", 10);
console.log('start: ' + times[0]);
console.log('end: ' + times[1]);
if (times[0] !== undefined) {
g.setFontAlign(-1, -1, 0);
var startdate = new Date(times[0]*1000);
g.drawString(startdate.local().as("0h:0m").str, 15, GraphYZero + 12);
g.setColor(1, 1, 0).setFont("Vector", 10);
log('startTime: ' + times[0]);
log('endTime: ' + times[1]);
if (times[0] !== 0) {
g.setFontAlign(-1, -1, 0).
drawString(getTimeString(times[0]*1000), 15, GraphYZero + 12);
}
if (times[1] !== undefined) {
g.setFontAlign(1, -1, 0);
var enddate = new Date(times[1]*1000);
g.drawString(enddate.local().as().str, GraphXMax, GraphYZero + 12);
if (times[1] !== 0) {
var dateStr = getDateString(times[1]*1000);
g.setFontAlign(-1, -1, 0).
drawString(dateStr, GraphXMax/2 - dateStr.length/2 - GraphMarkerOffset, GraphYZero + 12).
setFontAlign(1, -1, 0).
drawString(getTimeString(times[1]*1000), GraphXMax, GraphYZero + 12);
}
console.log("Finished rendering data");
log("Finished rendering data");
Bangle.buzz(200, 0.3);
g.flip();
setWatch(stop, BTN2, {edge:"falling", debounce:50, repeat:false});
return;
}
showMainMenu();

View File

@ -549,7 +549,26 @@ var locales = {
abday: "ned.,pon.,tor.,sre.,čet.,pet.,sob.",
day: "nedelja,ponedeljek,torek,sreda,četrtek,petek,sobota",
trans: { yes: "da", Yes: "Da", no: "ne", No: "Ne", ok: "ok", on: "Vklj.", off: "Izklj.", "< Back": "< Nazaj" }
}/*,
},
"pt_PT": {
lang: "pt_PT",
decimal_point: ",",
thousands_sep: " ",
currency_symbol: "€",
int_curr_symbol: "EUR",
speed: "kmh",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
datePattern: { 0: "%d %b %Y", 1: "%d/%m/%y" },
abmonth: "Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez",
month: "Janeiro,Fevereiro,Março,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro",
abday: "Dom,Seg,Ter,Qua,Qui,Sex,Sab",
day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado",
trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "ok", on: "on", off: "off" }
},
/*,
"he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399
codePage : "ISO8859-8",
lang: "he_IL",

View File

@ -1,2 +1,5 @@
0.01: Initial Release
0.02: Color Themes, Smoother scrolling
0.03: Added Spanish Language
0.04: Added German Language
0.05: BUGFIX: pedometer widget interfered with the clock Font Alignment

View File

@ -13,6 +13,8 @@ Use Button 1 (the top right button) to change the language
| English | English (Traditional) | French | Japanese (Romanji) |
| ---- | ---- | ---- | ---- |
| ![](./format-01.jpg) | ![](format-02.jpg) | ![](format-03.jpg) |![](format-04.jpg) |
| **German** | **Spanish** | | |
| ![](./format-05.jpg) | ![](format-06.jpg) | | |
### Button 3
Button 3 (bottom right button) is used to change the colour

View File

@ -4,7 +4,7 @@
</head>
<body>
<p>Please select watch languages</p>
<p>Please select watch languages (Max 3, only the first 3 selected will be loaded)</p>
<table id="language_selection">
<tr>
@ -23,9 +23,11 @@
{name:"English", shortname:"en"},
{name:"English(Traditional)",shortname:"en2"},
{name:"French",shortname:"fr"},
{name:"Japanese",shortname:"jp"}
{name:"Japanese",shortname:"jp"},
{name:"Spanish",shortname:"es"},
{name:"German",shortname:"de"}
];
var selected_languages = ["en","fr","jp"];
var selected_languages = ["en","es","jp"];
try{
var stored = localStorage.getItem('slidingtext_stored')
if(stored) selected_languages = JSON.parse(stored);
@ -50,7 +52,7 @@
for (var i=0; i<slidingtext_languages.length; i++) {
var curr_language = slidingtext_languages[i];
var checked=document.getElementById("enabled_"+i).checked;
if (checked) {
if (checked && new_selected_languages.length < 3 ) {
new_selected_languages.push(curr_language.shortname);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -121,23 +121,27 @@ class ShiftText {
setBgColor(bg_color){
this.bg_color = bg_color;
}
reset(){
reset(hard_reset) {
//console.log("reset");
this.hide();
this.x = this.init_x;
this.y = this.init_y;
this.txt = this.init_txt;
if (hard_reset) {
this.txt = this.init_txt;
}
this.show();
if(this.timeoutId != null){
clearTimeout(this.timeoutId);
}
}
show() {
g.setFontAlign(-1,-1,0);
g.setFont(this.font_name,this.font_size);
g.setColor(this.color[0],this.color[1],this.color[2]);
g.drawString(this.txt, this.x, this.y);
}
hide(){
g.setFontAlign(-1,-1,0);
g.setFont(this.font_name,this.font_size);
//console.log("bgcolor:" + this.bg_color);
g.setColor(this.bg_color[0],this.bg_color[1],this.bg_color[2]);
@ -249,12 +253,15 @@ function nextColorTheme(){
if(color_scheme_index >= row_displays.length){
color_scheme_index = 0;
}
var color_scheme = color_schemes[color_scheme_index];
setColorScheme(color_schemes[color_scheme_index]);
reset_clock(true);
draw_clock();
}
function setColorScheme(color_scheme){
setColor(color_scheme.main_bar,
color_scheme.other_bars,
color_scheme.background);
reset_clock();
draw_clock();
}
function setColor(main_color,other_color,bg_color){
@ -274,8 +281,7 @@ function setColor(main_color,other_color,bg_color){
]);
}
// load the date formats required
// load the date formats and laguages required
LANGUAGES_FILE = "slidingtext.languages.json";
var LANGUAGES_DEFAULT = ["en","en2"];
var locales = null;
@ -313,7 +319,7 @@ function changeFormatter(){
}
console.log("changing to formatter " + date_formatter_idx);
date_formatter = date_formatters[date_formatter_idx];
reset_clock();
reset_clock(true);
draw_clock();
command_stack_high_priority.unshift(
function() {
@ -340,23 +346,64 @@ function changeFormatter(){
}
var DISPLAY_TEXT_X = 20;
function reset_clock(hard_reset){
console.log("reset_clock hard_reset:" + hard_reset);
function reset_clock(){
//console.log("reset_clock");
for (var i = 0; i < row_displays.length; i++) {
row_displays[i].speed_x = CLOCK_TEXT_SPEED_X;
row_displays[i].reset();
setColorScheme(color_schemes[color_scheme_index]);
if(!hard_reset && last_draw_time != null){
// If its not a hard reset then we want to reset the
// rows set to the last time. If the last time is too long
// ago then we fast forward to 1 min ago.
// In this way the watch wakes by scrolling
// off the last time and scroll on the new time
var reset_time = last_draw_time;
var last_minute_millis = Date.now() - 60000;
if(reset_time.getTime() < last_minute_millis){
reset_time = display_time(new Date(last_minute_millis));
}
var rows = date_formatter.formatDate(reset_time);
for (var i = 0; i < rows.length; i++) {
row_displays[i].hide();
row_displays[i].speed_x = CLOCK_TEXT_SPEED_X;
row_displays[i].x = DISPLAY_TEXT_X;
row_displays[i].y = row_displays[i].init_y;
if(row_displays[i].timeoutId != null){
clearTimeout(row_displays[i].timeoutId);
}
row_displays[i].setText(rows[i]);
row_displays[i].show();
}
} else {
// do a hard reset and clear everything out
for (var i = 0; i < row_displays.length; i++) {
row_displays[i].speed_x = CLOCK_TEXT_SPEED_X;
row_displays[i].reset(hard_reset);
}
}
reset_commands();
}
let last_draw_time = null;
const next_minute_boundary_secs = 7.5;
const next_minute_boundary_secs = 10;
function display_time(date){
if(date.getSeconds() > 60 - next_minute_boundary_secs){
console.log("forwarding to next minute");
return new Date(date.getTime() + next_minute_boundary_secs * 1000);
} else {
return date;
}
}
function draw_clock(){
var date = new Date();
// we don't want the time to be displayed
// and then immediately be trigger another time
if(last_draw_time != null &&
date.getTime() - last_draw_time.getTime() < next_minute_boundary_secs * 1000 &&
Date.now() - last_draw_time.getTime() < next_minute_boundary_secs * 1000 &&
has_commands() ){
console.log("skipping draw clock");
return;
@ -364,13 +411,9 @@ function draw_clock(){
last_draw_time = date;
}
reset_commands();
console.log("draw_clock:" + date.toISOString());
// we don't want the time to be displayed
// and then immediately be trigger another time
if(date.getSeconds() > 60 - next_minute_boundary_secs){
console.log("forwarding to next minute");
date = new Date(date.getTime() + next_minute_boundary_secs * 1000);
}
date = display_time(date);
console.log("draw_clock:" + last_draw_time.toISOString() + " display:" + date.toISOString());
// for debugging only
//date.setMinutes(37);
var rows = date_formatter.formatDate(date);
var display;
@ -404,7 +447,7 @@ function display_row(display,txt){
//console.log("move in new:" + txt);
display.onFinished(next_command);
display.setTextXPosition(txt, 240);
display.moveToX(20);
display.moveToX(DISPLAY_TEXT_X);
}
);
}
@ -421,14 +464,14 @@ function display_row(display,txt){
//console.log("move in:" + txt);
display.onFinished(next_command);
display.setTextXPosition(txt,240);
display.moveToX(20);
display.moveToX(DISPLAY_TEXT_X);
}
);
} else {
command_stack_high_priority.push(
function(){
//console.log("move in2:" + txt);
display.setTextXPosition(txt,20);
display.setTextXPosition(txt,DISPLAY_TEXT_X);
next_command();
}
);
@ -445,10 +488,7 @@ function set_colorscheme(colorscheme_name){
if(color_schemes[i].name == colorscheme_name){
color_scheme_index = i;
console.log("match");
var color_scheme = color_schemes[color_scheme_index];
setColor(color_scheme.main_bar,
color_scheme.other_bars,
color_scheme.background);
setColorScheme(color_schemes[color_scheme_index]);
break;
}
}
@ -508,7 +548,7 @@ function button1pressed() {
function button3pressed() {
console.log("button3pressed");
nextColorTheme();
reset_clock();
reset_clock(true);
draw_clock();
save_settings();
}
@ -517,7 +557,7 @@ function button3pressed() {
let intervalRef = null;
function clearTimers(){
if(intervalRef) {
if(intervalRef != null) {
clearInterval(intervalRef);
intervalRef = null;
}
@ -532,22 +572,50 @@ function startTimers(){
draw_clock();
}
/**
* confirms that a redraw is needed by checking the last redraw time and
* the lcd state of the UI
* @returns {boolean|*}
*/
function shouldRedraw(){
return last_draw_time != null &&
Date.now() - last_draw_time.getTime() > next_minute_boundary_secs * 1000
&& Bangle.isLCDOn();
}
function scheduleDrawClock(){
//console.log("scheduleDrawClock");
if(intervalRef) clearTimers();
intervalRef = setInterval(draw_clock, 60*1000);
draw_clock();
clearTimers();
if (Bangle.isLCDOn()) {
console.log("schedule draw of clock");
intervalRef = setInterval(() => {
if (!shouldRedraw()) {
console.log("draw clock callback - skipped redraw");
} else {
console.log("draw clock callback");
draw_clock()
}
}, 60 * 1000
);
if (shouldRedraw()) {
draw_clock();
} else {
console.log("scheduleDrawClock - skipped redraw");
}
} else {
console.log("scheduleDrawClock - skipped not visible");
}
}
Bangle.on('lcdPower', (on) => {
if (on) {
console.log("lcdPower: on");
Bangle.drawWidgets();
reset_clock();
reset_clock(false);
startTimers();
} else {
console.log("lcdPower: off");
reset_clock();
reset_clock(false);
clearTimers();
}
});

View File

@ -0,0 +1,94 @@
var DateFormatter = require("slidingtext.dtfmt.js");
const germanNumberStr = [ ["ZERO",""], // 0
["EINS",""], // 1
["ZWEI",""], //2
["DREI",''], //3
["VIER",''], //4
["FÜNF",''], //5
["SECHS",''], //6
["SEIBEN",''], //7
["ACHT",''], //8
["NUEN",''], // 9,
["ZEHN",''], // 10
["ELF",''], // 11,
["ZWÖLF",''], // 12
["DREI",'ZEHN'], // 13
["VIER",'ZEHN'], // 14
["FÜNF",'ZEHN'], // 15
["SECH",'ZEHN'], // 16
["SIEB",'ZEHN'], // 17
["ACHT",'ZEHN'], // 18
["NEUN",'ZEHN'], // 19
];
const germanTensStr = ["ZERO",//0
"ZEHN",//10
"ZWANZIG",//20
"DREIßIG",//30
"VIERZIG",//40
"FÜNFZIG",//50
"SECHZIG"//60
]
const germanUnit = ["",//0
"EINUND",//1
"ZWEIUND",//2
"DREIUND",//3
"VIERUND", //4
"FÜNFUND", //5
"SECHSUND", //6
"SEIBENUND", //7
"ACHTUND", //8
"NEUNUND" //9
]
function germanHoursToText(hours){
hours = hours % 12;
if(hours == 0){
hours = 12;
}
return germanNumberStr[hours][0];
}
function germanMinsToText(mins) {
if (mins < 20) {
return germanNumberStr[mins];
} else {
var tens = (mins / 10 | 0);
var word1 = germanTensStr[tens];
var remainder = mins - tens * 10;
var word2 = germanUnit[remainder];
return [word2, word1];
}
}
class GermanDateFormatter extends DateFormatter {
constructor() { super();}
name(){return "German";}
formatDate(date){
var mins = date.getMinutes();
var hourOfDay = date.getHours();
var hours = germanHoursToText(hourOfDay);
//console.log('hourOfDay->' + hourOfDay + ' hours text->' + hours)
// Deal with the special times first
if(mins == 0){
var hours = germanHoursToText(hourOfDay);
return [hours,"UHR", "","",""];
} /*else if(mins == 30){
var hours = germanHoursToText(hourOfDay+1);
return ["", "", "HALB","", hours];
} else if(mins == 15){
var hours = germanHoursToText(hourOfDay);
return ["", "", "VIERTEL", "NACH",hours];
} else if(mins == 45) {
var hours = germanHoursToText(hourOfDay+1);
return ["", "", "VIERTEL", "VOR",hours];
} */ else {
var mins_txt = germanMinsToText(mins);
return [hours, "UHR", mins_txt[0],mins_txt[1]];
}
}
}
module.exports = GermanDateFormatter;

View File

@ -0,0 +1,77 @@
var DateFormatter = require("slidingtext.dtfmt.js");
const spanishNumberStr = [ ["ZERO"], // 0
["UNA",""], // 1
["DOS",""], //2
["TRES",''], //3
["CUATRO",''], //4
["CINCO",''], //5
["SEIS",''], //6
["SEITO",''], //7
["OCHO",''], //8
["NUEVE",''], // 9,
["DIEZ",''], // 10
["ONCE",''], // 11,
["DOCE",''], // 12
["TRECE",''], // 13
["CATORCE",''], // 14
["QUINCE",''], // 15
["DIECI",'SEIS'], // 16
["DIECI",'SIETE'], // 17
["DIECI",'OCHO'], // 18
["DIECI",'NEUVE'], // 19
["VEINTA",''], // 20
["VEINTI",'UNO'], // 21
["VEINTI",'DOS'], // 22
["VEINTI",'TRES'], // 23
["VEINTI",'CUATRO'], // 24
["VEINTI",'CINCO'], // 25
["VEINTI",'SEIS'], // 26
["VEINTI",'SIETE'], // 27
["VEINTI",'OCHO'], // 28
["VEINTI",'NUEVE'] // 29
];
function spanishHoursToText(hours){
hours = hours % 12;
if(hours == 0){
hours = 12;
}
return spanishNumberStr[hours][0];
}
function spanishMinsToText(mins){
return spanishNumberStr[mins];
}
class SpanishDateFormatter extends DateFormatter {
constructor() { super();}
name(){return "Spanish";}
formatDate(date){
var mins = date.getMinutes();
var hourOfDay = date.getHours();
if(mins > 30){
hourOfDay += 1;
}
var hours = spanishHoursToText(hourOfDay);
//console.log('hourOfDay->' + hourOfDay + ' hours text->' + hours)
// Deal with the special times first
if(mins == 0){
return [hours,"", "","",""];
} else if(mins == 30){
return [hours, "Y", "MEDIA",""];
} else if(mins == 15){
return [hours, "Y", "CUARTO",""];
} else if(mins == 45) {
return [hours, "MENOS", "CUARTO",""];
} else if(mins > 30){
var mins_txt = spanishMinsToText(60-mins);
return [hours, "MENOS", mins_txt[0],mins_txt[1]];
} else {
var mins_txt = spanishMinsToText(mins);
return [hours, "Y", mins_txt[0],mins_txt[1]];
}
}
}
module.exports = SpanishDateFormatter;

View File

@ -46,12 +46,12 @@ class FrenchDateFormatter extends DateFormatter {
} else if(mins == 30){
return [hours, heures,'ET DEMIE'];
} else if(mins == 15){
return [hours, heures,'ET QUERT'];
return [hours, heures,'ET QUART'];
} else if(mins == 45){
var next_hour = date.getHours() + 1;
hours = frenchHoursToText(next_hour);
heures = frenchHeures(next_hour);
return [hours, heures,"MOINS",'LET QUERT'];
return [hours, heures,"MOINS",'LET QUART'];
}
if(mins > 30){
var to_mins = 60-mins;