Merge branch 'espruino:master' into master

master
sir-indy 2022-04-06 13:22:35 +01:00 committed by GitHub
commit aaaf0b5be8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
126 changed files with 76921 additions and 341 deletions

View File

@ -1,12 +1,34 @@
// place your const, vars, functions or classes here
// special function to handle display switch on
Bangle.on('lcdPower', (on) => {
if (on) {
// call your app function here
// If you clear the screen, do Bangle.drawWidgets();
// clear the screen
g.clear();
var n = 0;
// redraw the screen
function draw() {
g.reset().clearRect(Bangle.appRect);
g.setFont("6x8").setFontAlign(0,0).drawString("Up / Down",g.getWidth()/2,g.getHeight()/2 - 20);
g.setFont("Vector",60).setFontAlign(0,0).drawString(n,g.getWidth()/2,g.getHeight()/2 + 30);
}
// Respond to user input
Bangle.setUI({mode: "updown"}, function(dir) {
if (dir<0) {
n--;
draw();
} else if (dir>0) {
n++;
draw();
} else {
n = 0;
draw();
}
});
g.clear();
// call your app function here
// First draw...
draw();
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"aclock.app.js","url":"clock-analog.js"},

View File

@ -14,3 +14,5 @@
0.13: Alarm widget state now updates when setting/resetting an alarm
0.14: Order of 'back' menu item
0.15: Fix hour/minute wrapping code for new menu system
0.16: Adding alarm library
0.17: Moving alarm internals to 'sched' library

6
apps/alarm/README.md Normal file
View File

@ -0,0 +1,6 @@
Default Alarm & Timer
======================
This allows you to add/modify any running timers.
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.

View File

@ -1,72 +0,0 @@
// Chances are boot0.js got run already and scheduled *another*
// 'load(alarm.js)' - so let's remove it first!
clearInterval();
function formatTime(t) {
var hrs = 0|t;
var mins = Math.round((t-hrs)*60);
return hrs+":"+("0"+mins).substr(-2);
}
function getCurrentHr() {
var time = new Date();
return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
}
function showAlarm(alarm) {
var msg = formatTime(alarm.hr);
var buzzCount = 10;
if (alarm.msg)
msg += "\n"+alarm.msg;
Bangle.loadWidgets();
Bangle.drawWidgets();
E.showPrompt(msg,{
title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!",
buttons : {/*LANG*/"Sleep":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins
}).then(function(sleep) {
buzzCount = 0;
if (sleep) {
if(alarm.ohr===undefined) alarm.ohr = alarm.hr;
alarm.hr += 10/60; // 10 minutes
} else {
alarm.last = (new Date()).getDate();
if (alarm.ohr!==undefined) {
alarm.hr = alarm.ohr;
delete alarm.ohr;
}
if (!alarm.rp) alarm.on = false;
}
require("Storage").write("alarm.json",JSON.stringify(alarms));
load();
});
function buzz() {
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
Bangle.buzz(100).then(()=>{
setTimeout(()=>{
Bangle.buzz(100).then(function() {
if (buzzCount--)
setTimeout(buzz, 3000);
else if(alarm.as) { // auto-snooze
buzzCount = 10;
setTimeout(buzz, 600000);
}
});
},100);
});
}
buzz();
}
// Check for alarms
var day = (new Date()).getDate();
var hr = getCurrentHr()+10000; // get current time - 10s in future to ensure we alarm if we've started the app a tad early
var alarms = require("Storage").readJSON("alarm.json",1)||[];
var active = alarms.filter(a=>a.on&&(a.hr<hr)&&(a.last!=day));
if (active.length) {
// if there's an alarm, show it
active = active.sort((a,b)=>a.hr-b.hr);
showAlarm(active[0]);
} else {
// otherwise just go back to default app
setTimeout(load, 100);
}

View File

@ -1,36 +1,43 @@
Bangle.loadWidgets();
Bangle.drawWidgets();
var alarms = require("Storage").readJSON("alarm.json",1)||[];
/*alarms = [
{ on : true,
hr : 6.5, // hours + minutes/60
msg : "Eat chocolate",
last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day!
rp : true, // repeat
as : false, // auto snooze
timer : 5, // OPTIONAL - if set, this is a timer and it's the time in minutes
}
];*/
var alarms = require("sched").getAlarms();
// An array of alarm objects (see sched/README.md)
// time in ms -> { hrs, mins }
function decodeTime(t) {
t = 0|t; // sanitise
var hrs = 0|(t/3600000);
return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) };
}
// time in { hrs, mins } -> ms
function encodeTime(o) {
return o.hrs*3600000 + o.mins*60000;
}
function formatTime(t) {
var hrs = 0|t;
var mins = Math.round((t-hrs)*60);
return hrs+":"+("0"+mins).substr(-2);
var o = decodeTime(t);
return o.hrs+":"+("0"+o.mins).substr(-2);
}
function formatMins(t) {
mins = (0|t)%60;
hrs = 0|(t/60);
return hrs+":"+("0"+mins).substr(-2);
}
function getCurrentHr() {
function getCurrentTime() {
var time = new Date();
return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
return (
time.getHours() * 3600000 +
time.getMinutes() * 60000 +
time.getSeconds() * 1000
);
}
function saveAndReload() {
require("sched").setAlarms(alarms);
require("sched").reload();
}
function showMainMenu() {
// Timer img "\0"+atob("DhKBAP////MDDAwwMGGBzgPwB4AeAPwHOBhgwMMzDez////w")
// Alarm img "\0"+atob("FBSBAABgA4YcMPDGP8Zn/mx/48//PP/zD/8A//AP/wD/8A//AP/wH/+D//w//8AAAADwAAYA")
const menu = {
'': { 'title': 'Alarm/Timer' },
/*LANG*/'< Back' : ()=>{load();},
@ -38,140 +45,151 @@ function showMainMenu() {
/*LANG*/'New Timer': ()=>editTimer(-1)
};
alarms.forEach((alarm,idx)=>{
if (alarm.timer) {
txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer);
} else {
txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr);
if (alarm.rp) txt += /*LANG*/" (repeat)";
}
menu[txt] = function() {
if (alarm.timer) editTimer(idx);
else editAlarm(idx);
var txt; // a leading space is currently required (JS error in Espruino 2v12)
if (alarm.timer)
txt = /*LANG*/"Timer"+" "+formatTime(alarm.timer);
else
txt = /*LANG*/"Alarm"+" "+formatTime(alarm.t);
if (alarm.rp) txt += "\0"+atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA=");
menu[txt] = {
value : "\0"+atob(alarm.on?"EhKBAH//v/////////////5//x//j//H+eP+Mf/A//h//z//////////3//g":"EhKBAH//v//8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA///3//g"),
onchange : function() {
if (alarm.timer) editTimer(idx, alarm);
else editAlarm(idx, alarm);
}
};
});
if (WIDGETS["alarm"]) WIDGETS["alarm"].reload();
return E.showMenu(menu);
}
function editAlarm(alarmIndex) {
function editDOW(dow, onchange) {
const menu = {
'': { 'title': /*LANG*/'Days of Week' },
'< Back' : () => onchange(dow)
};
for (var i = 0; i < 7; i++) (i => {
var dayOfWeek = require("locale").dow({ getDay: () => i });
menu[dayOfWeek] = {
value: !!(dow&(1<<i)),
format: v => v ? "Yes" : "No",
onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i),
};
})(i);
E.showMenu(menu);
}
function editAlarm(alarmIndex, alarm) {
var newAlarm = alarmIndex<0;
var hrs = 12;
var mins = 0;
var en = true;
var repeat = true;
var as = false;
if (!newAlarm) {
var a = alarms[alarmIndex];
hrs = 0|a.hr;
mins = Math.round((a.hr-hrs)*60);
en = a.on;
repeat = a.rp;
as = a.as;
var a = {
t : 12*3600000, // 12 o clock default
on : true,
rp : true,
as : false,
dow : 0b1111111,
last : 0,
vibrate : ".."
}
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
if (alarm) Object.assign(a,alarm);
var t = decodeTime(a.t);
const menu = {
'': { 'title': /*LANG*/'Alarm' },
/*LANG*/'< Back' : showMainMenu,
'< Back' : () => showMainMenu(),
/*LANG*/'Hours': {
value: hrs, min : 0, max : 23, wrap : true,
onchange: v => hrs=v
value: t.hrs, min : 0, max : 23, wrap : true,
onchange: v => t.hrs=v
},
/*LANG*/'Minutes': {
value: mins, min : 0, max : 59, wrap : true,
onchange: v => mins=v
value: t.mins, min : 0, max : 59, wrap : true,
onchange: v => t.mins=v
},
/*LANG*/'Enabled': {
value: en,
value: a.on,
format: v=>v?"On":"Off",
onchange: v=>en=v
onchange: v=>a.on=v
},
/*LANG*/'Repeat': {
value: en,
value: a.rp,
format: v=>v?"Yes":"No",
onchange: v=>repeat=v
onchange: v=>a.rp=v
},
/*LANG*/'Days': {
value: "SMTWTFS".split("").map((d,n)=>a.dow&(1<<n)?d:".").join(""),
onchange: () => editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)})
},
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
/*LANG*/'Auto snooze': {
value: as,
value: a.as,
format: v=>v?"Yes":"No",
onchange: v=>as=v
onchange: v=>a.as=v
}
};
function getAlarm() {
var hr = hrs+(mins/60);
var day = 0;
// If alarm is for tomorrow not today (eg, in the past), set day
if (hr < getCurrentHr())
day = (new Date()).getDate();
// Save alarm
return {
on : en, hr : hr,
last : day, rp : repeat, as: as
};
}
menu[/*LANG*/"> Save"] = function() {
if (newAlarm) alarms.push(getAlarm());
else alarms[alarmIndex] = getAlarm();
require("Storage").write("alarm.json",JSON.stringify(alarms));
menu[/*LANG*/"Save"] = function() {
a.t = encodeTime(t);
if (a.t < getCurrentTime())
a.day = (new Date()).getDate();
if (newAlarm) alarms.push(a);
else alarms[alarmIndex] = a;
saveAndReload();
showMainMenu();
};
if (!newAlarm) {
menu[/*LANG*/"> Delete"] = function() {
menu[/*LANG*/"Delete"] = function() {
alarms.splice(alarmIndex,1);
require("Storage").write("alarm.json",JSON.stringify(alarms));
saveAndReload();
showMainMenu();
};
}
return E.showMenu(menu);
}
function editTimer(alarmIndex) {
function editTimer(alarmIndex, alarm) {
var newAlarm = alarmIndex<0;
var hrs = 0;
var mins = 5;
var en = true;
if (!newAlarm) {
var a = alarms[alarmIndex];
mins = (0|a.timer)%60;
hrs = 0|(a.timer/60);
en = a.on;
var a = {
timer : 5*60*1000, // 5 minutes
on : true,
rp : false,
as : false,
dow : 0b1111111,
last : 0,
vibrate : ".."
}
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
if (alarm) Object.assign(a,alarm);
var t = decodeTime(a.timer);
const menu = {
'': { 'title': /*LANG*/'Timer' },
'< Back' : () => showMainMenu(),
/*LANG*/'Hours': {
value: hrs, min : 0, max : 23, wrap : true,
onchange: v => hrs=v
value: t.hrs, min : 0, max : 23, wrap : true,
onchange: v => t.hrs=v
},
/*LANG*/'Minutes': {
value: mins, min : 0, max : 59, wrap : true,
onchange: v => mins=v
value: t.mins, min : 0, max : 59, wrap : true,
onchange: v => t.mins=v
},
/*LANG*/'Enabled': {
value: en,
format: v=>v?/*LANG*/"On":/*LANG*/"Off",
onchange: v=>en=v
}
value: a.on,
format: v=>v?"On":"Off",
onchange: v=>a.on=v
},
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
};
function getTimer() {
var d = new Date(Date.now() + ((hrs*60)+mins)*60000);
var hr = d.getHours() + (d.getMinutes()/60) + (d.getSeconds()/3600);
// Save alarm
return {
on : en,
timer : (hrs*60)+mins,
hr : hr,
rp : false, as: false
};
}
menu["> Save"] = function() {
if (newAlarm) alarms.push(getTimer());
else alarms[alarmIndex] = getTimer();
require("Storage").write("alarm.json",JSON.stringify(alarms));
menu[/*LANG*/"Save"] = function() {
a.timer = encodeTime(t);
a.t = getCurrentTime() + a.timer;
if (newAlarm) alarms.push(a);
else alarms[alarmIndex] = a;
saveAndReload();
showMainMenu();
};
if (!newAlarm) {
menu["> Delete"] = function() {
menu[/*LANG*/"Delete"] = function() {
alarms.splice(alarmIndex,1);
require("Storage").write("alarm.json",JSON.stringify(alarms));
saveAndReload();
showMainMenu();
};
}

View File

@ -1,25 +0,0 @@
// check for alarms
(function() {
var alarms = require('Storage').readJSON('alarm.json',1)||[];
var time = new Date();
var active = alarms.filter(a=>a.on);
if (active.length) {
active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24);
var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
if (!require('Storage').read("alarm.js")) {
console.log("No alarm app!");
require('Storage').write('alarm.json',"[]");
} else {
var t = 3600000*(active[0].hr-hr);
if (active[0].last == time.getDate() || t < 0) t += 86400000;
if (t<1000) t=1000;
/* execute alarm at the correct time. We avoid execing immediately
since this code will get called AGAIN when alarm.js is loaded. alarm.js
will then clearInterval() to get rid of this call so it can proceed
normally. */
setTimeout(function() {
load("alarm.js");
},t);
}
}
})();

View File

@ -1,18 +1,17 @@
{
"id": "alarm",
"name": "Default Alarm & Timer",
"name": "Alarm & Timer",
"shortName": "Alarms",
"version": "0.15",
"description": "Set and respond to alarms and timers",
"version": "0.17",
"description": "Set alarms and timers on your Bangle",
"icon": "app.png",
"tags": "tool,alarm,widget",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"dependencies": {"scheduler":"type"},
"storage": [
{"name":"alarm.app.js","url":"app.js"},
{"name":"alarm.boot.js","url":"boot.js"},
{"name":"alarm.js","url":"alarm.js"},
{"name":"alarm.img","url":"app-icon.js","evaluate":true},
{"name":"alarm.wid.js","url":"widget.js"}
],
"data": [{"name":"alarm.json"}]
]
}

View File

@ -1,7 +1,8 @@
WIDGETS["alarm"]={area:"tl",width:0,draw:function() {
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
},reload:function() {
WIDGETS["alarm"].width = (require('Storage').readJSON('alarm.json',1)||[]).some(alarm=>alarm.on) ? 24 : 0;
// don't include library here as we're trying to use as little RAM as possible
WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0;
}
};
WIDGETS["alarm"].reload();

View File

@ -8,4 +8,5 @@
0.06: fixes #1271 - wrong settings name
when weekday name and calendar weeknumber are on then display is <weekday short> #<calweek>
week is buffered until date or timezone changes
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
0.08: fixed calendar weeknumber not shortened to two digits

View File

@ -99,7 +99,7 @@ function updateState() {
}
function isoStr(date) {
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).substr(-2) + "-" + ("0" + date.getDate()).substr(-2);
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2);
}
var calWeekBuffer = [false,false,false]; //buffer tz, date, week no (once calculated until other tz or date is requested)
@ -140,7 +140,7 @@ function draw() {
g.setFontAlign(0, 0).setFont("Anton").drawString(timeStr, x, y); // draw time
if (secondsScreen) {
y += 65;
var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).substr(-2);
var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).slice(-2);
if (doColor())
g.setColor(0, 0, 1);
g.setFont("AntonSmall");
@ -193,7 +193,7 @@ function draw() {
if (calWeek || weekDay) {
var dowcwStr = "";
if (calWeek)
dowcwStr = " #" + ("0" + ISO8601calWeek(date)).substring(-2);
dowcwStr = " #" + ("0" + ISO8601calWeek(date)).slice(-2);
if (weekDay)
dowcwStr = require("locale").dow(date, calWeek ? 1 : 0) + dowcwStr; //weekDay e.g. Monday or weekDayShort #<calWeek> e.g. Mon #01
else //week #01

View File

@ -1,7 +1,7 @@
{
"id": "antonclk",
"name": "Anton Clock",
"version": "0.07",
"version": "0.08",
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
"readme":"README.md",
"icon": "app.png",

2
apps/bee/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New app!
0.02: Fix bug with regenerating index, fix bug in word lookups

36
apps/bee/README.md Normal file
View File

@ -0,0 +1,36 @@
# Spelling bee game
Word finding game inspired by the NYT spelling bee. Find as many words with 4 or more letters (must include the
letter at the center of the 'hive') as you can.
## Usage
- tap on letters to type out word
- swipe left to delete last letter
- swipe right to enter; the word will turn blue while it is being checked against the internal dictionary; once
checked, it will turn red if the word is invalid, does not contain the central letter or has been guessed before or
will turn green if it is a valid word; in the latter case, points will be awarded
- swipe down to shuffle the 6 outer letters
- swipe up to view a list of already guessed words; tap on any of them to return to the regular game.
## Scoring
The number of correctly guessed words is displayed on the bottom left, the score on the bottom right. A single point
is awarded for a 4 letter word, or the number of letters if longer. A pangram is a word that contains all 7 letters at
least once and yields an additional 7 points. Each game contains at least one pangram.
## Technical remarks
The game uses an internal dictionary consisting of a newline separated list of English words ('bee.words', using the '2of12inf' word list).
The dictionary is fairly large (~700kB of flash space) and thus requires appropriate space on the watch and will make installing the app somewhat
slow. Because of its size it cannot be compressed (heatshrink needs to hold the compressed/uncompressed data in memory).
In order to make checking the validity of a guessed word faster an index file ('bee_lindex.json') is installed with
the app that facilitates faster word lookups. This index file is specific to the dictionary file used. If one were to
replace the dictionary file with a different version (e.g. a different language) the index file has to be regenerated. The easiest
way to do so is to delete (via the Web IDE or the fileman app on the watch) the file 'bee_lindex.json' - it will be regenerated (and saved,
i.e. it only happens once) on app startup automatically, a process that takes roughly 30 seconds.
![Screenshot](./bee_screenshot.png)

1
apps/bee/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AE2JAAIKHnc7DyNPp4vRGAwuBGB4sBAAQvSGIovPFqYvHGAYvDGBYsGGhwvGGIQvEGBQnDMYhkNGBAvOvQABqyRTF5GJr4wLFwQACX6IwLsowJLYMrldVGAQvTsoADGBITD0YvDldPF6n+F4gyGGAdP5nMF4KKBGDJZDGI7EBcoOiGAK7DGAQvYRogxEr1Pp9VMAiSBBILBWeJIxCromBMAQwDAAZfTGBQyCxOCGAIvBGIV/F7AwMAAOIp95GAYACFqoyQMAIwGF7QADEQd5FgIADqvGF8DnEAAIvFGIWjF8CFE0QwHAAQudAAK0EGBQuecw3GqpemYIxiCGIa8cF4wwHdTwvJp9/F82jGA9VMQovf5jkHGIwvg4wvIAAgvg5miF9wwNF8QABF9QwF0YuoF4oxCqoulGBAAB42i0QvjGBPMF0gwIFswwHF1IA/AH4A/AH4AL"))

BIN
apps/bee/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

195
apps/bee/bee.app.js Normal file
View File

@ -0,0 +1,195 @@
const S = require("Storage");
var letters = [];
var letterIdx = [];
var centers = [];
var word = '';
var foundWords = [];
var score = 0;
var intervalID = -1;
function prepareLetterIdx () {
"compile"
var li = [0];
if (S.read("bee_lindex.json")!==undefined) li = S.readJSON("bee_lindex.json"); // check for cached index
else {
for (var i=1; i<26; ++i) {
var prefix = String.fromCharCode(97+i%26);
console.log(prefix);
li.push(S.read('bee.words').indexOf("\n"+prefix, li[i-1])+1);
}
li.push(S.read('bee.words').length);
S.writeJSON("bee_lindex.json", li);
}
for (var i=0; i<26; ++i) letterIdx[i] = S.read("bee.words", li[i], li[i+1]-li[i]);
}
function findWord (w) {
"compile"
var ci = w.charCodeAt(0)-97;
var f = letterIdx[ci].indexOf("\n"+w+"\n");
if (f>=0) return true;
if (letterIdx[ci].substr(0, w.length)==w) return true;
return false;
}
function isPangram(w) {
var ltrs = '';
for (var i=0; i<w.length; ++i) if (ltrs.indexOf(w[i])===-1) ltrs += w[i];
return ltrs.length==7;
}
function checkWord (w) {
if (w.indexOf(String.fromCharCode(97+letters[0]))==-1) return false; // does it contain central letter?
if (foundWords.indexOf(w)>=0) return false; // already found
if (findWord(w)) {
foundWords.push(w);
foundWords.sort();
if (w.length==4) score++;
else score += w.length;
if (isPangram(w)) score += 7;
return true;
}
return false;
}
function getHexPoly(cx, cy, r, a) {
var p = [];
for (var i=0; i<6; ++i) p.push(cx+r*Math.sin((i+a)/3*Math.PI), cy+r*Math.cos((i+a)/3*Math.PI));
return p;
}
function drawHive() {
w = g.getWidth();
h = g.getHeight();
const R = w/3.3;
centers = getHexPoly(w/2, h/2+10, R, 0);
centers.push(w/2, h/2+10);
g.clear();
g.setFont("Vector", w/7).setFontAlign(0, 0, 0);
g.setColor(g.theme.fg);
for (var i=0; i<6; ++i) {
g.drawPoly(getHexPoly(centers[2*i], centers[2*i+1], 0.9*R/Math.sqrt(3), 0.5), {closed:true});
g.drawString(String.fromCharCode(65+letters[i+1]), centers[2*i]+2, centers[2*i+1]+2);
}
g.setColor(1, 1, 0).fillPoly(getHexPoly(w/2, h/2+10, 0.9*R/Math.sqrt(3), 0.5));
g.setColor(0).drawString(String.fromCharCode(65+letters[0]), w/2+2, h/2+10+2);
}
function shuffleLetters(qAll) {
for (var i=letters.length-1; i > 0; i--) {
var j = (1-qAll) + Math.floor(Math.random()*(i+qAll));
var temp = letters[i];
letters[i] = letters[j];
letters[j] = temp;
}
}
function pickLetters() {
var ltrs = "";
while (ltrs.length!==7) {
ltrs = [];
var j = Math.floor(26*Math.random());
var i = Math.floor((letterIdx[j].length-10)*Math.random());
while (letterIdx[j][i]!="\n" && i<letterIdx[j].length) ++i;
if (i<letterIdx[j].length-1) {
++i;
while (letterIdx[j][i]!=="\n") {
var c = letterIdx[j][i];
if (ltrs.indexOf(c)===-1) ltrs += c;
++i;
}
}
}
for (var i=0; i<7; ++i) letters.push(ltrs.charCodeAt(i)-97);
shuffleLetters(1);
}
function drawWord(c) {
g.clearRect(0, 0, g.getWidth()-1, 19);
g.setColor(c).setFont("Vector", 20).setFontAlign(0, 0, 0).drawString(word, g.getWidth()/2, 11).flip();
}
function touchHandler(e, x) {
var hex = 0;
var hex_d = 1e6;
for (var i=0; i<7; ++i) {
var d = (x.x-centers[2*i])*(x.x-centers[2*i]) + (x.y-centers[2*i+1])*(x.y-centers[2*i+1]);
if (d < hex_d) {
hex_d = d;
hex = i+1;
}
}
hex = hex%7;
if (word.length <= 15) word += String.fromCharCode(letters[hex]+65);
drawWord(g.theme.fg);
}
function drawScore() {
g.setColor(g.theme.fg).setFont("Vector", 20).setFontAlign(0, 0, 0);
g.clearRect(0, g.getHeight()-22, 60, g.getHeight()-1);
g.clearRect(g.getWidth()-60, g.getHeight()-22, g.getWidth(), g.getHeight()-1);
g.drawString(foundWords.length.toString(), 30, g.getHeight()-11);
g.drawString(score.toString(), g.getWidth()-30, g.getHeight()-11);
}
function wordFound (c) {
word = "";
drawWord(g.theme.fg);
drawScore();
clearInterval(intervalID);
intervalID = -1;
}
function swipeHandler(d, e) {
if (d==-1 && word.length>0) {
word = word.slice(0, -1);
drawWord(g.theme.fg);
}
if (d==1 && word.length>=4) {
drawWord("#00f");
drawWord((checkWord(word.toLowerCase()) ? "#0f0" : "#f00"));
if (intervalID===-1) intervalID = setInterval(wordFound, 800);
}
if (e===1) {
shuffleLetters(0);
drawHive();
drawScore();
drawWord(g.theme.fg);
}
if (e===-1 && foundWords.length>0) showWordList();
}
function showWordList() {
Bangle.removeListener("touch", touchHandler);
Bangle.removeListener("swipe", swipeHandler);
E.showScroller({
h : 20, c : foundWords.length,
draw : (idx, r) => {
g.clearRect(r.x,r.y,r.x+r.w-1,r.y+r.h-1).setFont("6x8:2");
g.setColor(isPangram(foundWords[idx])?'#0f0':g.theme.fg).drawString(foundWords[idx].toUpperCase(),r.x+10,r.y+4);
},
select : (idx) => {
setInterval(()=> {
E.showScroller();
drawHive();
drawScore();
drawWord(g.theme.fg);
Bangle.on("touch", touchHandler);
Bangle.on("swipe", swipeHandler);
clearInterval();
}, 100);
}
});
}
prepareLetterIdx();
pickLetters();
drawHive();
drawScore();
Bangle.on("touch", touchHandler);
Bangle.on("swipe", swipeHandler);

1
apps/bee/bee_lindex.json Normal file
View File

@ -0,0 +1 @@
[0,41048,80445,152390,198606,228714,257919,279071,303726,337982,343582,348026,367246,404452,419780,438696,496250,499697,544600,624304,659085,680996,691270,708186,708341,709916,710883]

BIN
apps/bee/bee_screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

74578
apps/bee/bee_words_2of12 Normal file

File diff suppressed because it is too large Load Diff

16
apps/bee/metadata.json Normal file
View File

@ -0,0 +1,16 @@
{ "id": "bee",
"name": "Bee",
"shortName":"Bee",
"icon": "app.png",
"version":"0.02",
"description": "Spelling bee",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"tags": "game,text",
"storage": [
{"name":"bee.app.js","url":"bee.app.js"},
{"name":"bee.words","url":"bee_words_2of12"},
{"name":"bee_lindex.json","url":"bee_lindex.json"},
{"name":"bee.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -7,6 +7,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"screenshots": [{"url":"berlin-clock-screenshot.png"}],
"storage": [

2
apps/bordle/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App
0.02: app keeps track of statistics now

View File

@ -21,7 +21,8 @@ function buttonPushed(b) {
layout.bt1.bgCol = wordle.keyColors.Z||g.theme.bg;
layout.bt2.label = "<del>";
layout.bt4.label = "<ent>";
layout.bt3.label = layout.bt5.label = " ";
layout.bt3.label = " ";
layout.bt5.label = "<stat>";
layout.bt6.label = "<";
}
}
@ -30,6 +31,10 @@ function buttonPushed(b) {
if (b!=6) {
if ((keyStateIdx<=5 || b<=1) && inp.length<5) inp += String.fromCharCode(b+(keyStateIdx-1)*5+64);
else if (layout.input.label.length>0 && b==2) inp = inp.slice(0,-1);
if (keyStateIdx==6 && b==5) {
wordle.drawStats();
return;
}
layout.input.label = inp;
}
layout = getKeyLayout(inp);
@ -82,6 +87,7 @@ class Wordle {
this.word = this.words.slice(i, i+5).toUpperCase();
}
console.log(this.word);
this.stats = require("Storage").readJSON("bordlestats.json") || {'1':0, '2':0, '3':0, '4':0, '5':0, '6':0, 'p':0, 'w':0, 's':0, 'ms':0};
}
render(clear) {
h = g.getHeight();
@ -109,7 +115,7 @@ class Wordle {
layout = getKeyLayout("");
wordle.render(true);
});
return 3;
return 1;
}
this.guesses.push(w);
this.nGuesses++;
@ -130,13 +136,39 @@ class Wordle {
this.guessColors[this.nGuesses].push(col);
}
if (correct==5) {
E.showAlert("The word is\n"+this.word, "You won in "+(this.nGuesses+1)+" guesses!").then(function(){load();});
return 1;
}
if (this.nGuesses==5) {
E.showAlert("The word was\n"+this.word, "You lost!").then(function(){load();});
E.showAlert("The word is\n"+this.word, "You won in "+(this.nGuesses+1)+" guesses!").then(function(){
wordle.stats['p']++; wordle.stats['w']++; wordle.stats['s']++; wordle.stats[wordle.nGuesses+1]++;
if (wordle.stats['s']>wordle.stats['ms']) wordle.stats['ms'] = wordle.stats['s'];
require("Storage").writeJSON("bordlestats.json", wordle.stats);
wordle.drawStats();
});
return 2;
}
if (this.nGuesses==5) {
E.showAlert("The word was\n"+this.word, "You lost!").then(function(){
wordle.stats['p']++; wordle.stats['s'] = 0;
require("Storage").writeJSON("bordlestats.json", wordle.stats);
wordle.drawStats();
});
return 3;
}
}
drawStats() {
E.showMessage(" ", "Statistics");
var max = 1;
for (i=1; i<=6; ++i) if (max<this.stats[i]) max = this.stats[i];
var h = g.getHeight();
var w = g.getWidth();
g.setColor('#00f').setFontVector((h-40)/8).setFontAlign(-1, 0, 0);
for (i=1; i<=6; ++i) {
tw = this.stats[i]*(w-24)/max;
g.setColor("#00f").fillRect(20, 52+(i-1)*(h-52)/6+2, 20+tw, 52+i*(h-52)/6-2);
g.setColor("#fff").drawString(i.toString(), 1, 52+(i-0.5)*(h-52)/6);
g.drawString(this.stats[i].toString(), tw>20 ? 25 : 25+tw, 52+(i-0.5)*(h-52)/6);
}
g.setFontVector((h-40)/9).setColor("#fff").drawString("P:"+this.stats["p"]+" W:"+this.stats["w"]+" S:"+this.stats["s"]+" M:"+this.stats["ms"], 4, 34);
Bangle.setUI();
Bangle.on("touch", (e) => { load(); });
}
}

View File

@ -2,7 +2,7 @@
"name": "Bordle",
"shortName":"Bordle",
"icon": "app.png",
"version":"0.01",
"version":"0.02",
"description": "Bangle version of a popular word search game",
"supports" : ["BANGLEJS2"],
"readme": "README.md",

View File

@ -8,6 +8,7 @@
"screenshots": [{"url":"screenshot_calculator.png"}],
"tags": "app,tool",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"calculator.app.js","url":"app.js"},
{"name":"calculator.img","url":"calculator-icon.js","evaluate":true}

View File

@ -9,6 +9,7 @@
"type": "clock",
"tags": "clock,cli,command,bash,shell",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"cliock.app.js","url":"app.js"},

View File

@ -3,3 +3,4 @@
0.03: fix metadata.json to allow setting as clock
0.04: added heart rate which is switched on when cycled to it through up/down touch on rhs
0.05: changed text to uppercase, just looks better, removed colons on text
0.06: better contrast for light theme, use fg color instead of dithered for ring

View File

@ -28,5 +28,6 @@ See [#1248](https://github.com/espruino/BangleApps/issues/1248)
## Screenshots
![](screenshot_daisy1.png)
![](screenshot_daisy3.png)
It is worth looking at the real thing though as the screenshot does not do it justice.
It is worth looking at the real thing though as the screenshots do not do it justice.

View File

@ -41,10 +41,17 @@ Graphics.prototype.setFontRoboto20 = function(scale) {
};
function assignPalettes() {
// palette for 0-40%
pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]);
// palette for 50-100%
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]);
if (g.theme.dark) {
// palette for 0-40%
pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]);
// palette for 50-100%
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]);
} else {
// palette for 0-40%
pal1 = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor(settings.fg), g.toColor("#00f")]);
// palette for 50-100%
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.theme.fg, g.toColor("#00f")]);
}
}
function setSmallFont20() {
@ -109,10 +116,10 @@ function updateSunRiseSunSet(now, lat, lon, line){
const infoData = {
ID_DATE: { calc: () => {var d = (new Date()).toString().split(" "); return d[2] + ' ' + d[1] + ' ' + d[3];} },
ID_DAY: { calc: () => {var d = require("locale").dow(new Date()).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} },
ID_SR: { calc: () => 'Sunrise ' + sunRise },
ID_SS: { calc: () => 'Sunset ' + sunSet },
ID_STEP: { calc: () => 'Steps ' + getSteps() },
ID_BATT: { calc: () => 'Battery ' + E.getBattery() + '%' },
ID_SR: { calc: () => 'SUNRISE ' + sunRise },
ID_SS: { calc: () => 'SUNSET ' + sunSet },
ID_STEP: { calc: () => 'STEPS ' + getSteps() },
ID_BATT: { calc: () => 'BATTERY ' + E.getBattery() + '%' },
ID_HRM: { calc: () => hrmCurrent }
};
@ -225,7 +232,7 @@ function drawSteps() {
setSmallFont();
g.setFontAlign(0,0);
g.setColor(g.theme.fg);
g.drawString('Steps ' + getSteps(), w/2, (3*h/4) - 4);
g.drawString('STEPS ' + getSteps(), w/2, (3*h/4) - 4);
drawingSteps = false;
}

View File

@ -1,13 +1,13 @@
{ "id": "daisy",
"name": "Daisy",
"version":"0.05",
"version":"0.06",
"dependencies": {"mylocation":"app"},
"description": "A clock based on the Pastel clock with large ring guage for steps",
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
"icon": "app.png",
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS2"],
"screenshots": [{"url":"screenshot_daisy2.jpg"}],
"screenshots": [{"url":"screenshot_daisy3.png"}],
"readme": "README.md",
"storage": [
{"name":"daisy.app.js","url":"app.js"},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -1,9 +1,9 @@
{
"id": "doztime",
"name": "Dozenal Time",
"shortName": "Dozenal Time",
"name": "Dozenal Digital Time",
"shortName": "Dozenal Digital",
"version": "0.05",
"description": "A dozenal Holocene calendar and dozenal diurnal clock",
"description": "A dozenal Holocene calendar and dozenal diurnal digital clock",
"icon": "app.png",
"type": "clock",
"tags": "clock",

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"ffcniftyb.app.js","url":"app.js"},

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"floralclk.app.js","url":"app.js"},

View File

@ -4,4 +4,6 @@
0.04: Bug fix score reset after Game Over, new icon
0.05: Chevron marker on the randomly added square
0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw
0.07: Optimized the mover algorithm for efficiency (work in progress)
0.07: Optimized the mover algorithm for efficiency (work in progress)
0.08: Bug fix at end of the game with victorious splash and glorious orchestra
0.09: Added settings menu, removed symbol selection button (*), added highscore reset

View File

@ -1,7 +1,7 @@
# Play the game of 1024
Move the tiles by swiping to the lefthand, righthand or up- and downward side of the watch.
Move the tiles by swiping left, right, up- or downward over the watchface.
When two tiles with the same number are squashed together they will add up as exponentials:
@ -21,16 +21,28 @@ Use the side **BTN** to exit the game, score and tile positions will be saved.
## Buttons on the screen
- Button **U**: Undo the last move. There are currently a maximum of 4 undo levels. The level is indicated with a small number in the lower righthand corner of the Undo button
- Button **\***: Change the text on the tile to number, capitals or Roman numbers
- Button **R**: Reset the game. The Higscore will be remembered. You will be prompted first.
- Button **U**: Undo the last move. There are currently a maximum of 9 undo levels. The level is indicated with a small number in the lower righthand corner of the Undo button
- You can set the maximum undo level in the Apps settings menu.
- Button **R**: Reset the game. The Highscore will be remembered. You will be prompted first.
- The highscore value can be reset in the Apps settings menu.
Apps setting: ![Screenshot of the apps settings menu](./game1024_sc_dump_app_settings.png)
- Stuff you can change in de 1024 Game settings:
- Symbols on the cells: numerical, alphabetical or Roman
- Undo levels [0-9]
- Exit: how to exit the game: long or short press
- Debug mode: on or off. This will log all kinds of stuff in the console of the Web IDE
- Reset Highsccore: Tired of looking at the old highscore? Now you can set it to 0 again.
### Credits
Game 1024 is based on Saming's 2048 and Misho M. Petkovic 1024game.org and conceptually similar to Threes by Asher Vollmer.
In Dark theme with numbers:
![Screenshot from the Banglejs 2 watch with the game in dark theme](./game1024_sc_dump_dark.png)
![Screenshot from the Banglejs 2 watch with the game in dark theme](./game1024_sc_dump_dark_v0.09.png)
In Light theme with characters:
![Screenshot from the Banglejs 2 watch with the game in light theme](./game1024_sc_dump_light.png)
![Screenshot from the Banglejs 2 watch with the game in light theme](./game1024_sc_dump_light.v0.09.png)

View File

@ -1,7 +1,21 @@
const debugMode = 'off'; // valid values are: off, test, production, development
let settings = Object.assign({
// default values
maxUndoLevels: 4,
charIndex: 0,
clockMode: true,
debugMode: false,
}, require('Storage').readJSON("game1024.settings.json", true) || {});
const clockMode = settings.clockMode!==undefined ? settings.clockMode : true;
const debugMode = settings.debugMode!==undefined ? settings.debugMode : false; // #settings -- valid values are: true or false
const maxUndoLevels = settings.maxUndoLevels!==undefined ? settings.maxUndoLevels : 4; // #settings
const charIndex = settings.charIndex!==undefined ? settings.charIndex : 0; // #settings -- plain numbers on the grid
delete settings; // remove unneeded settings from memory
const middle = {x:Math.floor(g.getWidth()/2)-20, y: Math.floor(g.getHeight()/2)};
const rows = 4, cols = 4;
const borderWidth = 6;
const rows = 4, cols = 4; // #settings
const borderWidth = 6;
const sqWidth = (Math.floor(Bangle.appRect.w - 48) / rows) - borderWidth;
const cellColors = [{bg:'#00FFFF', fg: '#000000'},
{bg:'#FF00FF', fg: '#000000'}, {bg:'#808000', fg: '#FFFFFF'}, {bg:'#0000FF', fg: '#FFFFFF'}, {bg:'#008000', fg: '#FFFFFF'},
@ -13,12 +27,8 @@ const cellChars = [
['0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
['0','I', 'II', 'III', 'IV', 'V', 'VI', 'VII','VIII', 'IX', 'X']
];
// const numInitialCells = 2;
const maxUndoLevels = 4;
const noExceptions = true;
let charIndex = 0; // plain numbers on the grid
const themeBg = g.theme.bg;
const themeBg = g.theme.bg;
const scores = {
currentScore: 0,
@ -78,12 +88,12 @@ const snapshot = {
updCounter: function() {
this.counter = ++this.counter > this.interval ? 0 : this.counter;
},
dump: {gridsize: rows * cols, expVals: [], score: 0, highScore: 0, charIndex: charIndex},
dump: {gridsize: rows * cols, expVals: [], score: 0, highScore: 0},
write: function() {
require("Storage").writeJSON(this.snFileName, this.dump);
},
read: function () {
let sn = require("Storage").readJSON(this.snFileName, noExceptions);
let sn = require("Storage").readJSON(this.snFileName, true);
if ((typeof sn == "undefined") || (sn.gridsize !== rows * cols)) {
require("Storage").writeJSON(this.snFileName, this.dump);
return false;
@ -101,7 +111,6 @@ const snapshot = {
});
this.dump.score = scores.currentScore;
this.dump.highScore = scores.highScore;
this.dump.charIndex = charIndex;
},
make: function () {
this.updCounter();
@ -118,7 +127,7 @@ const snapshot = {
});
scores.currentScore = this.dump.score ? this.dump.score : 0;
scores.highScore = this.dump.highScore ? this.dump.highScore : 0 ;
charIndex = this.dump.charIndex ? this.dump.charIndex : 0 ;
if (this.dump.hasOwnProperty('charIndex')) delete this.dump.charIndex; // depricated in v0.09
}
},
reset: function () {
@ -129,12 +138,11 @@ const snapshot = {
}
this.dump.score = 0;
this.dump.highScore = scores.highScore;
this.dump.charIndex = charIndex;
this.write();
debug(() => console.log("reset D U M P E D!", this.dump));
}
};
const btnAtribs = {x: 134, w: 42, h: 42, fg:'#C0C0C0', bg:'#800000'};
const btnAtribs = {x: 134, w: 42, h: 50, fg:'#C0C0C0', bg:'#800000'};
const buttons = {
all: [],
draw: function () {
@ -162,6 +170,7 @@ const buttons = {
*/
const mover = {
gameWon: false,
direction: {
up: {name: 'up', step: 1, innerBegin: 0, innerEnd: rows-1, outerBegin: 0, outerEnd: cols-1, iter: rows -1,
sqIndex: function (i,o) {return i*(cols) + o;}, sqNextIndex: function (i,o) {return i < rows -1 ? (i+1)*(cols) + o : -1;}
@ -313,7 +322,7 @@ class Cell {
}
drawBg() {
debug(()=>console.log("Drawbg!!"));
if (this.isRndm == true) {
if (this.isRndm) {
debug(()=>console.log('Random: (ax)', this.ax));
g.setColor(this.getColor(this.expVal).bg)
.fillRect(this.x0, this.y0, this.x1, this.y1)
@ -364,7 +373,7 @@ class Cell {
this.isRndm = true;
}
drawRndmIndicator(){
if (this.isRndm == true) {
if (this.isRndm) {
debug(()=>console.log('Random: (ax)', this.ax));
g.setColor(this.getColor(0).bg)
.fillPoly(this.ax,this.ay,this.bx,this.by,this.cx,this.cy);
@ -373,8 +382,9 @@ class Cell {
}
function undoGame() {
g.clear();
if (scores.lastScores.length > 0) {
if (scores.lastScores.length) {
g.clear();
allSquares.forEach(sq => {
sq.popFromUndo();
sq.drawBg();
@ -385,9 +395,9 @@ function undoGame() {
buttons.draw();
updUndoLvlIndex();
snapshot.make();
Bangle.loadWidgets();
Bangle.drawWidgets();
}
Bangle.loadWidgets();
Bangle.drawWidgets();
}
function addToUndo() {
allSquares.forEach(sq => {
@ -397,7 +407,7 @@ function addToUndo() {
}
function addToScore (val) {
scores.add(val);
if (val == 10) messageYouWin();
if (val == 10) mover.gameWon = true;
}
function createGrid () {
let cn =0;
@ -421,15 +431,30 @@ function messageGameOver () {
.drawString("O V E R !", middle.x+12, middle.y+25);
}
function messageYouWin () {
g.setColor("#1a0d00")
const c = (g.theme.dark) ? {"fg": "#FFFFFF", "bg": "#808080"} : {"fg": "#FF0000", "bg": "#000000"};
g.setColor(c.bg)
.setFont12x20(2)
.setFontAlign(0,0,0)
.drawString("YOU HAVE", middle.x+18, middle.y-24)
.drawString("W O N ! !", middle.x+18, middle.y+24);
g.setColor("#FF0808")
g.setColor(c.fg)
.drawString("YOU HAVE", middle.x+17, middle.y-25)
.drawString("W O N ! !", middle.x+17, middle.y+25);
Bangle.buzz(200, 1);
for (let r=0;r<4;r++){
Bangle.buzz(200,0.2)
.then((result) => {
Bangle.buzz(200,0.5)
.then((result)=>{
Bangle.buzz(200,0.8)
.then((result)=>{
Bangle.buzz(200,1)
.then((result)=>{
Bangle.buzz(500,0);
})
})
})
})
}
}
function makeRandomNumber () {
return Math.ceil(2*Math.random());
@ -471,8 +496,8 @@ function initGame() {
drawGrid();
scores.draw();
buttons.draw();
// Clock mode allows short-press on button to exit
Bangle.setUI("clock");
// #settings Clock mode allows short-press on button to exit
if(clockMode) Bangle.setUI("clock");
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
@ -491,8 +516,8 @@ function drawPopUp(message,cb) {
rDims.x+10, rDims.y2-40
]);
buttons.all.forEach(btn => {btn.disable();});
const btnYes = new Button('yes', rDims.x+16, rDims.y2-80, 54, btnAtribs.h, 'YES', btnAtribs.fg, btnAtribs.bg, cb, true);
const btnNo = new Button('no', rDims.x2-80, rDims.y2-80, 54, btnAtribs.h, 'NO', btnAtribs.fg, btnAtribs.bg, cb, true);
const btnYes = new Button('yes', rDims.x+16, rDims.y2-88, 54, btnAtribs.h, 'YES', btnAtribs.fg, btnAtribs.bg, cb, true);
const btnNo = new Button('no', rDims.x2-80, rDims.y2-88, 54, btnAtribs.h, 'NO', btnAtribs.fg, btnAtribs.bg, cb, true);
btnYes.draw();
btnNo.draw();
g.setColor('#000000');
@ -527,6 +552,7 @@ function handlePopUpClicks(btn) {
function resetGame() {
g.clear();
scores.reset();
mover.gameWon=false;
allSquares.forEach(sq => {sq.setExpVal(0);sq.removeUndo();sq.setRndmFalse();});
addRandomNumber();
addRandomNumber();
@ -543,14 +569,8 @@ function resetGame() {
* @param {function} func function to call like console.log()
*/
const debug = (func) => {
switch (debugMode) {
case "development":
if (typeof func === 'function') {
func();
}
break;
case "off":
default: break;
if (debugMode) {
if (typeof func === 'function') func();
}
};
@ -653,6 +673,12 @@ function runGame(dir){
debug(() => console.log("G A M E O V E R !!"));
snapshot.reset();
messageGameOver();
} else {
if (mover.gameWon) {
debug(() => console.log("Y O U H A V E W O N !!"));
snapshot.reset();
messageYouWin();
}
}
}
@ -667,13 +693,9 @@ function updUndoLvlIndex() {
.drawString(scores.lastScores.length, x, y);
}
}
function incrCharIndex() {
charIndex++;
if (charIndex >= cellChars.length) charIndex = 0;
drawGrid();
}
buttons.add(new Button('undo', btnAtribs.x, 25, btnAtribs.w, btnAtribs.h, 'U', btnAtribs.fg, btnAtribs.bg, undoGame, true));
buttons.add(new Button('chars', btnAtribs.x, 71, btnAtribs.w, 31, '*', btnAtribs.fg, btnAtribs.bg, function(){incrCharIndex();}, true));
buttons.add(new Button('restart', btnAtribs.x, 106, btnAtribs.w, btnAtribs.h, 'R', btnAtribs.fg, btnAtribs.bg, function(){drawPopUp('Do you want\nto restart?',handlePopUpClicks);}, true));
initGame();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,7 +1,7 @@
{ "id": "game1024",
"name": "1024 Game",
"shortName" : "1024 Game",
"version": "0.07",
"version": "0.09",
"icon": "game1024.png",
"screenshots": [ {"url":"screenshot.png" } ],
"readme":"README.md",
@ -12,6 +12,7 @@
"supports" : ["BANGLEJS2"],
"storage": [
{"name":"game1024.app.js","url":"app.js"},
{"name":"game1024.settings.js","url":"settings.js"},
{"name":"game1024.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

70
apps/game1024/settings.js Normal file
View File

@ -0,0 +1,70 @@
(function(back) {
var FILE = "game1024.settings.json";
var scoreFile = "game1024.json";
// Load settings
var settings = Object.assign({
maxUndoLevels: 5,
charIndex: 0,
clockMode: true,
debugMode: false,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
var symbols = ["1 2 3 ...", "A B C ...", "I II III..."];
var settingsMenu = {
"" : { "title" : "1024 Game" },
"< Back" : () => back(),
"Symbols": {
value: 0|settings.charIndex,
min:0,max:symbols.length-1,
format: v=>symbols[v],
onchange: v=> { settings.charIndex=v; writeSettings();}
}
,
"Undo levels:": {
value: 0|settings.maxUndoLevels, // 0| converts undefined to 0
min: 0, max: 9,
onchange: v => {
settings.maxUndoLevels = v;
writeSettings();
}
},
"Exit press:": {
value: !settings.debugMode, // ! converts undefined to true
format: v => v?"short":"long",
onchange: v => {
settings.debugMode = v;
writeSettings();
},
},
"Debug mode:": {
value: !!settings.debugMode, // !! converts undefined to false
format: v => v?"On":"Off",
onchange: v => {
settings.debugMode = v;
writeSettings();
}
},
"Reset Highscore": () => {
E.showPrompt('Reset Highscore?').then((v) => {
let delay = 50;
if (v) {
delay = 500;
let sF = require("Storage").readJSON(scoreFile, true);
if (typeof sF !== "undefined") {
E.showMessage('Resetting');
sF.highScore = 0;
require("Storage").writeJSON(scoreFile, sF);
} else {
E.showMessage('No highscore!');
}
}
setTimeout(() => E.showMenu(settingsMenu), delay);
});
}
}
// Show the menu
E.showMenu(settingsMenu);
})

2
apps/glbasic/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.20: New App!

View File

@ -0,0 +1,114 @@

Graphics.prototype.setFontLECO1976Regular42 = function(scale) {
// Actual height 42 (41 - 0)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60+(scale<<8)+(1<<16));
};
Graphics.prototype.setFontLECO1976Regular22 = function(scale) {
// Actual height 22 (21 - 0)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16));
};
require("Font7x11Numeric7Seg").add(Graphics);
var temperature = 13;
//the following 2 sections are used from waveclk to schedule minutely updates
// timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function drawBackground() {
g.setBgColor(0,0,0);
g.setColor(1,1,1);
g.clear();
}
function digit(num){
return String.fromCharCode(num + 48);
}
function timeString(h, m){
return digit(h/10) + digit(h%10) + ":" + digit(m/10) + digit(m%10);
}
function dayString(w){
return digit(w/10) + digit(w%10);
}
function getSteps() {
if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps();
}
return '????';
}
function draw(){
drawBackground();
var date = new Date();
var h = date.getHours(), m = date.getMinutes();
var d = date.getDate(), w = date.getDay();
g.setBgColor(0,0,0);
g.setColor(1,1,1);
// g.setFont('Vector', 30);
// g.setFont("7x11Numeric7Seg", 5);
g.setFontLECO1976Regular42();
g.setFontAlign(0, -1);
g.drawString(timeString(h, m), g.getWidth()/2,28);
g.drawString(dayString(w), g.getWidth()*3/4,88);
g.setColor(0,1,0);
g.fillRect(0,76,g.getWidth(), 80);
g.reset();
// Steps
g.setFontLECO1976Regular22();
g.setFontAlign(-1, -1);
g.drawString(getSteps(), 8, 88);
// g.drawString(temperature, 4, 108);
// widget redraw
Bangle.drawWidgets();
queueDraw();
}
////////////////////////////////////////////////////
// Bangle.setBarometerPower(true);
Bangle.loadWidgets();
draw();
// Bangle.on('pressure', function(e){
// temperature = e.temperature;
// draw();
// });
//the following section is also from waveclk
Bangle.on('lcdPower',on=>{
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
Bangle.setUI("clock");
Bangle.drawWidgets();

202
apps/glbasic/glbasic.app.js Normal file
View File

@ -0,0 +1,202 @@
Graphics.prototype.setFontLECO1976Regular42 = function (scale) {
// Actual height 42 (41 - 0)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60 + (scale << 8) + (1 << 16));
};
Graphics.prototype.setFontLECO1976Regular22 = function (scale) {
// Actual height 22 (21 - 0)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22 + (scale << 8) + (1 << 16));
};
require("Font7x11Numeric7Seg").add(Graphics);
// the following 2 sections are used from waveclk to schedule minutely updates
// timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function () {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function drawBackground() {
g.setBgColor(0, 0, 0);
g.setColor(1, 1, 1);
g.clear();
}
function digit(num) {
return String.fromCharCode(num + 48);
}
function timeString(h, m) {
return digit(h / 10) + digit(h % 10) + ":" + digit(m / 10) + digit(m % 10);
}
function dayString(w) {
return digit(w / 10) + digit(w % 10);
}
function getSteps() {
if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps();
}
return '????';
}
/**
* draws calender week view (-1,0,1) for given date
*/
function drawCal() {
d = /*this.date ? this.date : */ new Date();
const DAY_NAME_FONT_SIZE = 10;
const CAL_Y = g.getHeight() - 44; // Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3;
const CAL_AREA_H = 44; // g.getHeight()-CAL_Y+24; //+24: top widgtes only
const CELL_W = g.getWidth() / 7; //cell width
const CELL_H = (CAL_AREA_H - DAY_NAME_FONT_SIZE) / 3; //cell heigth
const DAY_NUM_FONT_SIZE = Math.min(CELL_H + 3, 15); //size down, max 15
const wdStrt = 1;
const ABR_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const IS_SUNDAY = [1, 0, 0, 0, 0, 1, 1]; // what days are sunday?
const nrgb = ["#000", "#FFF", "#F00", "#0F0", "#00F", "#FF0"]; //fg, r ,g , b
const suClr = 5; // sunday color fg
const tdyMrkClr = 3; // today bk
const tdyNumClr = 0; // today fg
g.setFont("Vector", DAY_NAME_FONT_SIZE + 3);
g.setColor(nrgb[1]);
g.setFontAlign(-1, -1);
// g.clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H);
//draw grid & Headline
const dNames = ABR_DAY.map((a) => a.length <= 2 ? a : a.substr(0, 2)); //force shrt 2
for (var dNo = 0; dNo < dNames.length; dNo++) {
const dIdx = wdStrt >= 0 ? ((wdStrt + dNo) % 7) : ((dNo + d.getDay() + 4) % 7);
const dName = dNames[dIdx];
// if(dNo>0) { g.drawLine(dNo*CELL_W, CAL_Y, dNo*CELL_W, CAL_Y+CAL_AREA_H-1);}
var colTx = 0;
var colBk = 1;
if (IS_SUNDAY[dIdx]) {
colBk = suClr;
}
g.setColor(nrgb[colBk]);
g.fillRect(dNo * CELL_W, CAL_Y, dNo * CELL_W + CELL_W, CAL_Y + DAY_NAME_FONT_SIZE - 1);
g.setColor(nrgb[colTx]);
g.drawString(dName, dNo * CELL_W + (CELL_W - g.stringWidth(dName)) / 2 + 2, CAL_Y - 1);
// g.setColor(nrgb[clTxt]);
}
g.setColor(nrgb[1]);
var nextY = CAL_Y + DAY_NAME_FONT_SIZE;
// horizontal lines
// for(i=0; i<3; i++){ const y=nextY+i*CELL_H; g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y); }
g.setFont("Vector", DAY_NUM_FONT_SIZE);
g.setFont("7x11Numeric7Seg", 1);
//write days
const tdyDate = d.getDate();
const days = wdStrt >= 0 ? 7 + ((7 + d.getDay() - wdStrt) % 7) : 10; //start day (week before=7 days + days in this week realtive to week start) or fixed 7+3 days
var rD = new Date(d.getTime());
rD.setDate(rD.getDate() - days);
var rDate = rD.getDate();
for (var y = 0; y < 3; y++) {
for (var x = 0; x < dNames.length; x++) {
if (rDate === tdyDate) { //today
g.setColor(nrgb[tdyMrkClr]); //today marker color or fg color
// rectangle
g.fillRect(x * CELL_W, nextY + CELL_H - 1, x * CELL_W + CELL_W, nextY + CELL_H + CELL_H - 1);
g.setColor(nrgb[tdyNumClr]); //today color or fg color
// simulate "bold"
g.drawString(rDate, 1 + x * CELL_W + ((CELL_W - g.stringWidth(rDate)) / 2) + 2, nextY + ((CELL_H - DAY_NUM_FONT_SIZE + 2) / 2) + (CELL_H * y));
} else if (IS_SUNDAY[rD.getDay()]) { //sundays
g.setColor(nrgb[suClr]);
} else { //default
g.setColor(nrgb[1]);
}
g.drawString(rDate, x * CELL_W + ((CELL_W - g.stringWidth(rDate)) / 2) + 2, nextY + ((CELL_H - DAY_NUM_FONT_SIZE + 2) / 2) + (CELL_H * y));
rD.setDate(rDate + 1);
rDate = rD.getDate();
}
}
}
function draw() {
g.reset();
drawBackground();
var date = new Date();
var h = date.getHours(),
m = date.getMinutes();
var d = date.getDate(),
w = date.getDay(); // d=1..31; w=0..6
g.setBgColor(0, 0, 0);
g.setColor(1, 1, 1);
// g.setFont('Vector', 30);
// g.setFont("7x11Numeric7Seg", 5);
g.setFontLECO1976Regular42();
g.setFontAlign(0, -1);
g.drawString(timeString(h, m), g.getWidth() / 2, 28);
g.drawString(dayString(d), g.getWidth() * 3 / 4, 88);
g.setColor(0, 1, 0);
g.fillRect(0, 76, g.getWidth(), 80);
g.reset();
// Steps
g.setFontLECO1976Regular22();
g.setFontAlign(-1, -1);
g.drawString(getSteps(), 8, 88);
drawCal();
// widget redraw
Bangle.drawWidgets();
queueDraw();
}
////////////////////////////////////////////////////
// Bangle.setBarometerPower(true);
Bangle.loadWidgets();
draw();
// Bangle.on('pressure', function(e){
// temperature = e.temperature;
// draw();
// });
//the following section is also from waveclk
Bangle.on('lcdPower', on => {
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
Bangle.setUI("clock");
Bangle.drawWidgets();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4UA///ssp4XthFCBwUBqoABqAaGBZcFBZdX1W1qgLHrwLKqv/6oLJAAILHioLJn5qBAAYLEBQoLeHQQABv4LjGAgLYq2qAAOlBbBHFBdPAKcQLdWcb7jAAoLcn4LKgEVHQVUBQsAgoLLq//6oLIr2q2oXJBZQvCqALGgILTA="))

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
apps/glbasic/icon48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,17 @@
{
"id": "glbasic",
"name": "GLBasic Clock",
"shortName": "GLBasic",
"version": "0.20",
"description": "A clock with large numbers",
"dependencies": {"widpedom":"app"},
"icon": "icon48.png",
"screenshots": [{"url":"glbasic_screenshot.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"glbasic.app.js","url":"glbasic.app.js"},
{"name":"glbasic.img","url":"glbasic.icon.js","evaluate":true}
]
}

View File

@ -8,6 +8,7 @@
"tags": "clock",
"screenshots": [{"url":"bangle1-high-contrast-clock-screenshot.png"}],
"supports": ["BANGLEJS"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"hcclock.app.js","url":"hcclock.app.js"},

View File

@ -7,6 +7,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}],
"allow_emulator": true,
"storage": [

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"intclock.app.js","url":"app.js"},

View File

@ -7,6 +7,7 @@
"icon": "intervals.png",
"tags": "",
"supports": ["BANGLEJS"],
"readme": "README.md",
"storage": [
{"name":"intervals.app.js","url":"intervals.app.js"},
{"name":"intervals.img","url":"intervals-icon.js","evaluate":true}

View File

@ -7,6 +7,7 @@
"tags": "tool,system,ios,apple,messages,notifications",
"dependencies": {"messages":"app"},
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"ios.app.js","url":"app.js"},
{"name":"ios.img","url":"app-icon.js","evaluate":true},

1
apps/kbswipe/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

36
apps/kbswipe/README.md Normal file
View File

@ -0,0 +1,36 @@
# Swipe Keyboard
A library that provides the ability to input text by swiping PalmOS Graffiti-style characters onto the screen.
To get a legend of available characters, just tap the screen.
## Usage
In your app's metadata, add:
```
"dependencies": {"textinput":"type"},
```
From inside your app, call:
```
Bangle.loadWidgets();
Bangle.drawWidgets();
require("textinput").input({text:"Foo"}).then(result => {
console.log("Text input", E.toJS(result));
});
```
The first argument to `input` is an object containing the following:
* `text` - initial text to edit
(in the future, the ability to restrict usage of newline/etc may be added)
## Make your own
You can create your own keyboard input apps. Just ensure that they have
`"type":"textinput",` in their metadata and provide a library called `textinput`
that exports an `input` method.

BIN
apps/kbswipe/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

110
apps/kbswipe/lib.js Normal file
View File

@ -0,0 +1,110 @@
/* To make your own strokes, type:
Bangle.on('stroke',print)
on the left of the IDE, then do a stroke and copy out the Uint8Array line
*/
exports.getStrokes = function(cb) {
cb("a", new Uint8Array([58, 159, 58, 155, 62, 144, 69, 127, 77, 106, 86, 90, 94, 77, 101, 68, 108, 62, 114, 59, 121, 59, 133, 61, 146, 70, 158, 88, 169, 107, 176, 124, 180, 135, 183, 144, 185, 152]));
cb("b", new Uint8Array([51, 47, 51, 77, 56, 123, 60, 151, 65, 163, 68, 164, 68, 144, 67, 108, 67, 76, 72, 43, 104, 51, 121, 74, 110, 87, 109, 95, 131, 117, 131, 140, 109, 152, 88, 157]));
cb("c", new Uint8Array([153, 62, 150, 62, 145, 62, 136, 62, 123, 62, 106, 65, 85, 70, 65, 75, 50, 82, 42, 93, 37, 106, 36, 119, 36, 130, 40, 140, 49, 147, 61, 153, 72, 156, 85, 157, 106, 158, 116, 158]));
cb("d", new Uint8Array([57, 178, 57, 176, 55, 171, 52, 163, 50, 154, 49, 146, 47, 135, 45, 121, 44, 108, 44, 97, 44, 85, 44, 75, 44, 66, 44, 58, 44, 48, 44, 38, 46, 31, 48, 26, 58, 21, 75, 20, 99, 26, 120, 35, 136, 51, 144, 70, 144, 88, 137, 110, 124, 131, 106, 145, 88, 153]));
cb("e", new Uint8Array([150, 72, 141, 69, 114, 68, 79, 69, 48, 77, 32, 81, 31, 85, 46, 91, 73, 95, 107, 100, 114, 103, 83, 117, 58, 134, 66, 143, 105, 148, 133, 148, 144, 148]));
cb("f", new Uint8Array([157, 52, 155, 52, 148, 52, 137, 52, 124, 52, 110, 52, 96, 52, 83, 52, 74, 52, 67, 52, 61, 52, 57, 52, 55, 52, 52, 52, 52, 54, 52, 58, 52, 64, 54, 75, 58, 97, 59, 117, 60, 130]));
cb("g", new Uint8Array([160, 66, 153, 62, 129, 58, 90, 56, 58, 57, 38, 65, 31, 86, 43, 125, 69, 152, 116, 166, 145, 154, 146, 134, 112, 116, 85, 108, 97, 106, 140, 106, 164, 106]));
cb("h", new Uint8Array([58, 50, 58, 55, 58, 64, 58, 80, 58, 102, 58, 122, 58, 139, 58, 153, 58, 164, 58, 171, 58, 177, 58, 179, 58, 181, 58, 180, 58, 173, 58, 163, 59, 154, 61, 138, 64, 114, 68, 95, 72, 84, 80, 79, 91, 79, 107, 82, 123, 93, 137, 111, 145, 130, 149, 147, 150, 154, 150, 159]));
cb("i", new Uint8Array([89, 48, 89, 49, 89, 51, 89, 55, 89, 60, 89, 68, 89, 78, 89, 91, 89, 103, 89, 114, 89, 124, 89, 132, 89, 138, 89, 144, 89, 148, 89, 151, 89, 154, 89, 156, 89, 157, 89, 158]));
cb("j", new Uint8Array([130, 57, 130, 61, 130, 73, 130, 91, 130, 113, 130, 133, 130, 147, 130, 156, 130, 161, 130, 164, 130, 166, 129, 168, 127, 168, 120, 168, 110, 168, 91, 167, 81, 167, 68, 167]));
cb("k", new Uint8Array([149, 63, 147, 68, 143, 76, 136, 89, 126, 106, 114, 123, 100, 136, 86, 147, 72, 153, 57, 155, 45, 152, 36, 145, 29, 131, 26, 117, 26, 104, 27, 93, 30, 86, 35, 80, 45, 77, 62, 80, 88, 96, 113, 116, 130, 131, 140, 142, 145, 149, 148, 153]));
cb("l", new Uint8Array([42, 55, 42, 59, 42, 69, 44, 87, 44, 107, 44, 128, 44, 143, 44, 156, 44, 163, 44, 167, 44, 169, 45, 170, 49, 170, 59, 169, 76, 167, 100, 164, 119, 162, 139, 160, 163, 159]));
cb("m", new Uint8Array([49, 165, 48, 162, 46, 156, 44, 148, 42, 138, 42, 126, 42, 113, 43, 101, 45, 91, 47, 82, 49, 75, 51, 71, 54, 70, 57, 70, 61, 74, 69, 81, 75, 91, 84, 104, 94, 121, 101, 132, 103, 137, 106, 130, 110, 114, 116, 92, 125, 75, 134, 65, 139, 62, 144, 66, 148, 83, 151, 108, 155, 132, 157, 149]));
cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49]));
cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62]));
cb("p", new Uint8Array([52, 59, 52, 64, 54, 73, 58, 88, 61, 104, 65, 119, 67, 130, 69, 138, 71, 145, 71, 147, 71, 148, 71, 143, 70, 133, 68, 120, 67, 108, 67, 97, 67, 89, 68, 79, 72, 67, 83, 60, 99, 58, 118, 58, 136, 63, 146, 70, 148, 77, 145, 84, 136, 91, 121, 95, 106, 97, 93, 97, 82, 97]));
cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67]));
cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150]));
cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158]));
cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146]));
cb("u", new Uint8Array([35, 52, 35, 59, 35, 73, 35, 90, 36, 114, 38, 133, 42, 146, 49, 153, 60, 157, 73, 158, 86, 156, 100, 152, 112, 144, 121, 131, 127, 114, 132, 97, 134, 85, 135, 73, 136, 61, 136, 56]));
cb("v", new Uint8Array([36, 55, 37, 59, 40, 68, 45, 83, 51, 100, 58, 118, 64, 132, 69, 142, 71, 149, 73, 156, 76, 158, 77, 160, 77, 159, 80, 151, 82, 137, 84, 122, 86, 111, 90, 91, 91, 78, 91, 68, 91, 63, 92, 61, 97, 61, 111, 61, 132, 61, 150, 61, 162, 61]));
cb("w", new Uint8Array([33, 58, 34, 81, 39, 127, 44, 151, 48, 161, 52, 162, 57, 154, 61, 136, 65, 115, 70, 95, 76, 95, 93, 121, 110, 146, 119, 151, 130, 129, 138, 84, 140, 56, 140, 45]));
cb("x", new Uint8Array([56, 63, 56, 67, 57, 74, 60, 89, 66, 109, 74, 129, 85, 145, 96, 158, 107, 164, 117, 167, 128, 164, 141, 155, 151, 140, 159, 122, 166, 105, 168, 89, 170, 81, 170, 73, 169, 66, 161, 63, 141, 68, 110, 83, 77, 110, 55, 134, 47, 145]));
cb("y", new Uint8Array([42, 56, 42, 70, 48, 97, 62, 109, 85, 106, 109, 90, 126, 65, 134, 47, 137, 45, 137, 75, 127, 125, 98, 141, 70, 133, 65, 126, 92, 137, 132, 156, 149, 166]));
cb("z", new Uint8Array([29, 62, 35, 62, 43, 62, 63, 62, 87, 62, 110, 62, 125, 62, 134, 62, 138, 62, 136, 63, 122, 68, 103, 77, 85, 91, 70, 107, 59, 120, 50, 132, 47, 138, 43, 143, 41, 148, 42, 151, 53, 155, 80, 157, 116, 158, 146, 158, 163, 158]));
cb("\b", new Uint8Array([183, 103, 182, 103, 180, 103, 176, 103, 169, 103, 159, 103, 147, 103, 133, 103, 116, 103, 101, 103, 85, 103, 73, 103, 61, 103, 52, 103, 38, 103, 34, 103, 29, 103, 27, 103, 26, 103, 25, 103, 24, 103]));
cb(" ", new Uint8Array([39, 118, 40, 118, 41, 118, 44, 118, 47, 118, 52, 118, 58, 118, 66, 118, 74, 118, 84, 118, 94, 118, 104, 117, 114, 116, 123, 116, 130, 116, 144, 116, 149, 116, 154, 116, 158, 116, 161, 116, 163, 116]));
};
exports.input = function(options) {
options = options||{};
var text = options.text;
if ("string"!=typeof text) text="";
Bangle.strokes = {};
exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
var flashToggle = false;
const R = Bangle.appRect;
function draw(noclear) {
g.reset();
if (!noclear) g.clearRect(R);
var l = g.setFont("6x8:4").wrapString(text+(flashToggle?"_":" "), R.w-8);
if (l.length>4) l=l.slice(-4);
g.drawString(l.join("\n"),R.x+4,R.y+4);
}
function show() {
g.reset();
g.clearRect(R).setColor("#f00");
var n=0;
exports.getStrokes((id,s) => {
var x = n%6;
var y = (n-x)/6;
s = g.transformVertices(s, {scale:0.16, x:R.x+x*30-4, y:R.y+y*30-4});
g.fillRect(s[0]-1,s[1]-2,s[0]+1,s[1]+1);
g.drawPoly(s);
n++;
});
}
function strokeHandler(o) {
//print(o);
if (!flashInterval)
flashInterval = setInterval(() => {
flashToggle = !flashToggle;
draw();
}, 1000);
if (o.stroke!==undefined) {
var ch = o.stroke;
if (ch=="\b") text = text.slice(0,-1);
else text += ch;
}
flashToggle = true;
draw();
}
Bangle.on('stroke',strokeHandler);
g.reset().clearRect(R);
show();
draw(true);
var flashInterval;
return new Promise((resolve,reject) => {
var l;//last event
Bangle.setUI({mode:"custom", drag:e=>{
if (l) g.reset().setColor("#f00").drawLine(l.x,l.y,e.x,e.y);
l = e.b ? e : 0;
},touch:() => {
if (flashInterval) clearInterval(flashInterval);
flashInterval = undefined;
show();
}, back:()=>{
Bangle.removeListener("stroke", strokeHandler);
clearInterval(flashInterval);
Bangle.setUI();
g.clearRect(Bangle.appRect);
resolve(text);
}});
});
};

View File

@ -0,0 +1,14 @@
{ "id": "kbswipe",
"name": "Swipe keyboard",
"version":"0.01",
"description": "A library for text input via PalmOS style swipe gestures (beta!)",
"icon": "app.png",
"type":"textinput",
"tags": "keyboard",
"supports" : ["BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"}],
"readme": "README.md",
"storage": [
{"name":"textinput","url":"lib.js"}
]
}

BIN
apps/kbswipe/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

1
apps/kbtouch/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

37
apps/kbtouch/README.md Normal file
View File

@ -0,0 +1,37 @@
# Touch Keyboard
A library that provides an on-screen keyboard for text input.
## Usage
In your app's metadata, add:
```
"dependencies": {"textinput":"type"},
```
From inside your app, call:
```
Bangle.loadWidgets();
Bangle.drawWidgets();
require("textinput").input({text:"Foo"}).then(result => {
console.log("Text input", E.toJS(result));
});
```
The first argument to `input` is an object containing the following:
* `text` - initial text to edit
(in the future, the ability to restrict usage of newline/etc may be added)
## Make your own
You can create your own keyboard input apps. Just ensure that they have
`"type":"textinput",` in their metadata and provide a library called `textinput`
that exports an `input` method.
## To-do
Make this Bangle.js 1 compatible (use left/right touch and up/down buttons)

BIN
apps/kbtouch/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

132
apps/kbtouch/lib.js Normal file
View File

@ -0,0 +1,132 @@
exports.input = function(options) {
options = options||{};
var text = options.text;
if ("string"!=typeof text) text="";
// Key Maps for Keyboard
var KEYMAPLOWER = [
"`1234567890-=\b",
"\2qwertyuiop[]\n",
"\2asdfghjkl;'#\n",
" \\zxcvbnm,./ ",
];
var KEYMAPUPPER = [
"¬!\"£$%^&*()_+\b",
"\2QWERTYUIOP{}\n",
"\2ASDFGHJKL:@~\n",
" |ZXCVBNM<>? ",
];
var KEYIMGL = Graphics.createImage(`
#
###
#####
#
#
#
#
#
#
#
#
#
#
#
#
#
`);KEYIMGL.transparent=0;
var KEYIMGR = Graphics.createImage(`
#
##
#####
##
#
###
#
#
#
#
#
#####
###
#
#`);KEYIMGR.transparent=0;
/* If a char in the keymap is >=128,
subtract 128 and look in this array for
multi-character key codes*/
var KEYEXTRA = [
String.fromCharCode(27,91,68), // 0x80 left
String.fromCharCode(27,91,67), // 0x81 right
String.fromCharCode(27,91,65), // 0x82 up
String.fromCharCode(27,91,66), // 0x83 down
String.fromCharCode(27,91,53,126), // 0x84 page up
String.fromCharCode(27,91,54,126), // 0x85 page down
];
// state
const R = Bangle.appRect;
var kbx = 0, kby = 0, kbdx = 0, kbdy = 0, kbShift = false, flashToggle = false;
const PX=12, PY=16, DRAGSCALE=24;
var xoff = 3, yoff = g.getHeight()-PY*4;
function draw() {
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
//g.drawImage(KEYIMG,0,yoff);
g.reset().setFont("6x8:2");
g.clearRect(R);
if (kbx>=0)
g.setColor(g.theme.bgH).fillRect(xoff+kbx*PX,yoff+kby*PY, xoff+(kbx+1)*PX-1,yoff+(kby+1)*PY-1).setColor(g.theme.fg);
g.drawImage(KEYIMGL,xoff,yoff+PY,{scale:2});
g.drawImage(KEYIMGR,xoff+PX*13,yoff,{scale:2});
g.drawString(map[0],xoff,yoff);
g.drawString(map[1],xoff,yoff+PY);
g.drawString(map[2],xoff,yoff+PY*2);
g.drawString(map[3],xoff,yoff+PY*3);
var l = g.setFont("6x8:4").wrapString(text+(flashToggle?"_":" "), R.w-8);
if (l.length>2) l=l.slice(-2);
g.drawString(l.join("\n"),R.x+4,R.y+4);
g.flip();
}
g.reset().clearRect(R);
draw();
var flashInterval = setInterval(() => {
flashToggle = !flashToggle;
draw();
}, 1000);
return new Promise((resolve,reject) => {
Bangle.setUI({mode:"custom", drag:e=>{
kbdx += e.dx;
kbdy += e.dy;
var dx = Math.round(kbdx/DRAGSCALE), dy = Math.round(kbdy/DRAGSCALE);
kbdx -= dx*DRAGSCALE;
kbdy -= dy*DRAGSCALE;
if (dx || dy) {
kbx = (kbx+dx+15)%15;
kby = (kby+dy+4)%4;
draw();
}
},touch:()=>{
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
var ch = map[kby][kbx];
if (ch=="\2") kbShift=!kbShift;
else if (ch=="\b") text = text.slice(0,-1);
else text += ch;
Bangle.buzz(20);
draw();
},back:()=>{
clearInterval(flashInterval);
Bangle.setUI();
g.clearRect(Bangle.appRect);
resolve(text);
}});
});
};

View File

@ -0,0 +1,14 @@
{ "id": "kbtouch",
"name": "Touch keyboard",
"version":"0.01",
"description": "A library for text input via onscreen keyboard",
"icon": "app.png",
"type":"textinput",
"tags": "keyboard",
"supports" : ["BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"}],
"readme": "README.md",
"storage": [
{"name":"textinput","url":"lib.js"}
]
}

BIN
apps/kbtouch/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -39,11 +39,13 @@ these conversions */
const charFallbacks = {
"ą":"a",
"ā":"a",
"å":"a",
"č":"c",
"ć":"c",
"ě":"e",
"ę":"e",
"ē":"e",
"æ":"e",
"ģ":"g",
"i":"ī",
"ķ":"k",
@ -53,6 +55,7 @@ const charFallbacks = {
"ņ":"n",
"ő":"o",
"ó":"o",
"ø":"o",
"ř":"r",
"ś":"s",
"š":"s",
@ -681,6 +684,24 @@ var locales = {
day: "Pirmdiena,Otrdiena,Trešdiena,Ceturtdiena,Piektdiena,Sestdiena,Svētdiena",
trans: { yes: "jā", Yes: "Jā", no: "nē", No: "Nē", ok: "labi", on: "Ieslēgt", off: "Izslēgt", "< Back": "< Atpakaļ" }
},
"no_NB": { // Using charfallbacks
lang: "no_NB",
decimal_point: ",",
thousands_sep: " ",
currency_symbol: "kr",
int_curr_symbol: "NOK",
speed: "kmh",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "", 1: "" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20
abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des",
month: "Januar,Februar,Mars,April,Mai,Juni,Juli,August,September,Oktober,November,Desember",
abday: "Ma,Ti,On,To,Fr,Lø,Sø",
day: "Mandag,Tirsdag,Onsdag,Torsdag,Fredag,Lørdag,Søndag",
trans: { yes: "ja", Yes: "Ja", no: "nei", No: "Nei", ok: "ok", on: "på", off: "av", "< Back": "< Tilbake", "Delete": "Slett", "Mark Unread": "Merk som ulest" }
},
/*,
"he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399
codePage : "ISO8859-8",

1
apps/megadenti/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Create New App !

2
apps/megadenti/README.md Normal file
View File

@ -0,0 +1,2 @@
Denti :
This teeth washing assistan helps you to wash your teeth

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4kA///A4M79/6823gvb70/qvLrXrrXdqmyzl2gvTzn0glS4ttuZh9iMQCykBvsRC6vUC6sdppIUgMb2Ol+IYBGaBGB6Oll1xGKMRpsfi/FrQXRjtHutai/uC6EBunRj9SrB5DPppeBj/njVV0IICu1xUpscqvBvgSCC5qlCuN1kqnCI5xGB69VwP1qJ2QUoMR4tcuOBA4LXOUoOKi/lq8RgMd64YMUoXCr8XFgMXs1p6JeM7cRjVS0PZtN5s2Z3oXKiKlBivHjl4swACu4XJgIWB6mxv0l0MZC4eZ3YXHKgPdolLuMR+tXC4lmzq/GQQNEonduILBDwOWC4lmBgLRG3vREQcBFwoABtfRC4kXaIKyFjAHBjtmy4EBvoXFjtBNA0X13u0N5j/O5+BEwhGBDwZICgPykUil0RwUikfqC4kR3YGCgOGwIuBCwIAB+NSkXh/3xIwlIiEBC4MbDgMaCoMl8U+AQNajXhIwcUIwIXBI4ceC4UcmVXJoMfC4nUVooABjhGC0LEBJAM+C4alBew5HCGAPu8QEBC4bsB2IXLAAk+0KlHXwoXLUop1BgK/CmQXG9ynCjtHIwUB3ZLDgJaCC4qJBLwJGDC4zvCAAbvBBoUReYZHCMAcTCYWvGgMu95CDRo7AFl8RiqmCCZSQGqKtCRwYAMgLqB4JYBmXuwIXP4UiRIXj9xyDCpTDBuUuYoWDRwYAKWIUVl4SCjXnC5I5DjORCQMxCQUf8JdNZITMEAggAvA="))

99
apps/megadenti/app.js Normal file
View File

@ -0,0 +1,99 @@
var i = 0;
var counter = 10;
var counterInterval;
var img = Graphics.createImage(`
##### #####
# ##### #
# #
# #
## ##
## ##
## ##
# #### #
# # # #
# # # #
## ##
## ##
`);
var img1 = Graphics.createImage(`
### # ##### ## ####
# # # # # # #
# # ### # # ####
# # # ###### # #
### #### ##### # # # #
##### #####
# ##### #
# #
# #
## ##
## ##
## ##
# #### #
# # # #
# # # #
## ##
## ##
`);
g.setColor('#012345');
function outOfTime() {
if (counterInterval) return;
E.showMessage("Out of Time", "My Timer");
Bangle.beep(200, 4000)
.then(() => new Promise(resolve => setTimeout(resolve,200)))
.then(() => Bangle.beep(200, 3000));
// again, 10 secs later
setTimeout(outOfTime, 10000);
g.setColor('#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'));
}
function immagine(){
g.drawImage(img1, 90, 20, {scale:2});
}
function countDown() {
counter--;
// Out of time
if (counter<=0) {
clearInterval(counterInterval);
counterInterval = undefined;
setWatch(startTimer, (process.env.HWVERSION==2) ? BTN1 : BTN2);
g.clear(img);
outOfTime();
return;
}
g.clear(1);
g.setFontAlign(0,0); // center font
g.setFont("Vector",80); // vector font, 80px
// draw the current counter value
g.drawImage(img, 90, 20, {scale:2});
g.drawString(counter,120,120);
g.drawLine(50,50,180,50);
g.drawLine(50,51,180,51);
g.drawLine(50,52,180,52);
// optional - this keeps the watch LCD lit up
Bangle.setLCDPower(1);
if (counter<=5){
immagine();
}
}
function startTimer() {
counter = 10;
countDown();
if (!counterInterval)
counterInterval = setInterval(countDown, 1000);
}
startTimer();

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,15 @@
{ "id": "megadenti",
"name": "Denti",
"shortName":"My Denti",
"icon": "brush-teeth.png",
"version":"0.01",
"description": "This app allows you wash your teeth in an efficent way. A coloured timer guides you while your smile becomes bright!",
"tags": "game",
"supports": ["BANGLEJS"],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{"name":"megadenti.app.js","url":"app.js"},
{"name":"megadenti.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -40,3 +40,6 @@
0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550)
0.26: Setting to auto-open music
0.27: Add 'mark all read' option to popup menu (fix #1624)
0.28: Option to auto-unlock the watch when a new message arrives
0.29: Fix message list overwrites on Bangle.js 1 (fix #1642)
0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel)

View File

@ -20,6 +20,7 @@ to the clock where a ringing bell will be shown in the Widget bar.
is chosen if there isn't much message text, but this specifies the smallest the font should get before
it starts getting clipped.
* `Auto-Open Music` - Should the app automatically open when the phone starts playing music?
* `Unlock Watch` - Should the app unlock the watch when a new message arrives, so you can touch the buttons at the bottom of the app?
## New Messages

View File

@ -82,31 +82,45 @@ function getNegImage() {
return atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA=");
}
/*
* icons should be 24x24px with 1bpp colors and transparancy
* icons should be 24x24px with 1bpp colors and 'Transparency to Color'
* http://www.espruino.com/Image+Converter
*/
function getMessageImage(msg) {
if (msg.img) return atob(msg.img);
var s = (msg.src||"").toLowerCase();
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA");
if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA");
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA");
if (s=="facebook") return getFBIcon();
if (s=="gmail") return getNotificationImage();
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA=");
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA==");
if (s=="gmail") return getNotificationImage();
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44");
if (s=="mail") return getNotificationImage();
if (s=="messenger") return getFBIcon();
if (s=="outlook mail") return getNotificationImage();
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA=");
if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA=");
if (s=="post & dhl") return atob("GBgBAPgAE/5wMwZ8NgN8NgP4NgP4HgP4HgPwDwfgD//AB/+AAf8AAAAABs7AHcdgG4MwAAAAGESAFESAEkSAEnyAEkSAFESAGETw");
if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA");
if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA==");
if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA==");
if (s=="sms message") return getNotificationImage();
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA");
if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA");
if (s=="telegram") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
if (s=="to do") return atob("GBgBAAAAAAAAAAAwAAB4AAD8AAH+AAP/DAf/Hg//Px/+f7/8///4///wf//gP//AH/+AD/8AB/4AA/wAAfgAAPAAAGAAAAAAAAAA");
if (s=="twitch") return atob("GBgBH//+P//+P//+eAAGeAAGeAAGeDGGeDOGeDOGeDOGeDOGeDOGeDOGeAAOeAAOeAAcf4/4f5/wf7/gf//Af/+AA/AAA+AAAcAA");
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA==");
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
if (s=="youtube") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
if (msg.id=="back") return getBackImage();
return getNotificationImage();
@ -115,28 +129,38 @@ function getMessageImageCol(msg,def) {
return {
// generic colors, using B2-safe colors
"alarm": "#fff",
"calendar": "#f00",
"mail": "#ff0",
"music": "#f0f",
"phone": "#0f0",
"sms message": "#0ff",
// brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos)
// all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?)
"bibel": "#54342c",
"discord": "#738adb",
"facebook": "#4267b2",
"gmail": "#ea4335",
"google home": "#fbbc05",
"home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
"hangouts": "#1ba261",
"home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
"instagram": "#dd2a7b",
"liferando": "#ee5c00",
"messenger": "#0078ff",
"nina": "#e57004",
"outlook mail": "#0072c6",
"post & dhl": "#f2c101",
"signal": "#00f",
"skype": "#00aff0",
"slack": "#e51670",
"threema": "#000",
"snapchat": "#ff0",
"teams": "#464eb8",
"telegram": "#0088cc",
"threema": "#000",
"to do": "#3999e5",
"twitch": "#6441A4",
"twitter": "#1da1f2",
"whatsapp": "#4fce5d",
"wordfeud": "#e7d3c7",
"youtube": "#f00",
}[(msg.src||"").toLowerCase()]||(def !== undefined?def:g.theme.fg);
}
@ -457,6 +481,7 @@ function checkMessages(options) {
var msg = MESSAGES[idx-1];
if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH);
else g.setColor(g.theme.fg);
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
if (idx==0) msg = {id:"back", title:"< Back"};
if (!msg) return;
var x = r.x+2, title = msg.title, body = msg.body;

View File

@ -57,15 +57,23 @@ exports.pushMessage = function(event) {
// otherwise load messages/show widget
var loadMessages = Bangle.CLOCK || event.important;
// first, buzz
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
if (!quiet && loadMessages && global.WIDGETS && WIDGETS.messages)
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
var unlockWatch = (require('Storage').readJSON('messages.settings.json',1)||{}).unlockWatch;
if (!quiet && loadMessages && global.WIDGETS && WIDGETS.messages){
WIDGETS.messages.buzz();
if(unlockWatch != false){
Bangle.setLocked(false);
Bangle.setLCDPower(1); // turn screen on
}
}
// after a delay load the app, to ensure we have all the messages
if (exports.messageTimeout) clearTimeout(exports.messageTimeout);
exports.messageTimeout = setTimeout(function() {
exports.messageTimeout = undefined;
// if we're in a clock or it's important, go straight to messages app
if (loadMessages) return load("messages.app.js");
if (loadMessages){
return load("messages.app.js");
}
if (!quiet && (!global.WIDGETS || !WIDGETS.messages)) return Bangle.buzz(); // no widgets - just buzz to let someone know
WIDGETS.messages.show();
}, 500);

View File

@ -1,7 +1,7 @@
{
"id": "messages",
"name": "Messages",
"version": "0.27",
"version": "0.30",
"description": "App to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",

View File

@ -4,6 +4,7 @@
if (settings.vibrate===undefined) settings.vibrate=".";
if (settings.repeat===undefined) settings.repeat=4;
if (settings.unreadTimeout===undefined) settings.unreadTimeout=60;
settings.unlockWatch=!!settings.unlockWatch;
settings.openMusic=!!settings.openMusic;
settings.maxUnreadTimeout=240;
return settings;
@ -14,18 +15,10 @@
require('Storage').writeJSON("messages.settings.json", settings);
}
var vibPatterns = [/*LANG*/"Off", ".", "-", "--", "-.-", "---"];
var mainmenu = {
"" : { "title" : /*LANG*/"Messages" },
"< Back" : back,
/*LANG*/'Vibrate': {
value: Math.max(0,vibPatterns.indexOf(settings().vibrate)),
min: 0, max: vibPatterns.length,
format: v => vibPatterns[v]||"Off",
onchange: v => {
updateSetting("vibrate", vibPatterns[v]);
}
},
/*LANG*/'Vibrate': require("buzz_menu").pattern(settings().vibrate, v => updateSetting("vibrate", v)),
/*LANG*/'Repeat': {
value: settings().repeat,
min: 0, max: 10,
@ -49,6 +42,11 @@
format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("openMusic", v)
},
/*LANG*/'Unlock Watch': {
value: !!settings().unlockWatch,
format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("unlockWatch", v)
},
};
E.showMenu(mainmenu);
})

View File

@ -32,14 +32,7 @@ draw:function() {
Bangle.drawWidgets();
},buzz:function() {
if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode
let v = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || ".";
function b() {
var c = v[0];
v = v.substr(1);
if (c==".") Bangle.buzz().then(()=>setTimeout(b,100));
if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100));
}
b();
require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || ".");
},touch:function(b,c) {
var w=WIDGETS["messages"];
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+w.iconwidth) return;

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "numerals,clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"screenshots": [{"url":"bangle1-numerals-screenshot.png"}],
"storage": [

View File

@ -7,6 +7,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS"],
"readme": "README.md",
"allow_emulator": true,
"screenshots": [{"url":"bangle1-pipboy-themed-clock-screenshot.png"}],
"storage": [

View File

@ -7,6 +7,7 @@
"type": "boot",
"tags": "system",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"screenshots": [{"url":"pro-menu-screenshot.png"}],
"storage": [
{"name":"promenu.boot.js","url":"boot.js","supports": ["BANGLEJS"]},

View File

@ -4,7 +4,7 @@
"shortName": "Q Alarm",
"icon": "app.png",
"version": "0.04",
"description": "Alarm and timer app with days of week and 'hard' option.",
"description": "[Not recommended - use 'Alarm & Timer' app] Alarm and timer app with days of week and 'hard' option.",
"tags": "tool,alarm,widget",
"supports": ["BANGLEJS", "BANGLEJS2"],
"storage": [

View File

@ -10,4 +10,5 @@
0.09: Fix broken start/stop if recording not enabled (fix #1561)
0.10: Don't allow the same setting to be chosen for 2 boxes (fix #1578)
0.11: Notifications fixes
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)

View File

@ -14,7 +14,8 @@ the red `STOP` in the bottom right turns to a green `RUN`.
shown will increase, even if you are standing still.
* `TIME` - the elapsed time for your run
* `PACE` - the number of minutes it takes you to run a given distance, configured in settings (default 1km) **based on your run so far**
* `HEART` - Your heart rate
* `HEART (BPM)` - Your current heart rate
* `Max BPM` - Your maximum heart rate reached during the run
* `STEPS` - Steps since you started exercising
* `CADENCE` - Steps per second based on your step rate *over the last minute*
* `GPS` - this is green if you have a GPS lock. GPS is turned on automatically
@ -35,7 +36,7 @@ Under `Settings` -> `App` -> `Run` you can change settings for this app.
record GPS/HRM/etc data every time you start a run?
* `Pace` is the distance that pace should be shown over - 1km, 1 mile, 1/2 Marathon or 1 Marathon
* `Boxes` leads to a submenu where you can configure what is shown in each of the 6 boxes on the display.
Available stats are "Time", "Distance", "Steps", "Heart (BPM)", "Pace (avg)", "Pace (curr)", "Speed", and "Cadence".
Available stats are "Time", "Distance", "Steps", "Heart (BPM)", "Max BPM", "Pace (avg)", "Pace (curr)", "Speed", and "Cadence".
Any box set to "-" will display no information.
* Box 1 is the top left (defaults to "Distance")
* Box 2 is the top right (defaults to "Time")

View File

@ -1,6 +1,6 @@
{ "id": "run",
"name": "Run",
"version":"0.12",
"version":"0.13",
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
"icon": "app.png",
"tags": "run,running,fitness,outdoors,gps",

View File

@ -42,11 +42,6 @@
value: Math.max(statsIDs.indexOf(settings[boxID]),0),
format: v => statsList[v].name,
onchange: v => {
for (var i=1;i<=6;i++)
if (settings["B"+i]==statsIDs[v]) {
settings["B"+i]="";
boxMenu["Box "+i].value=0;
}
settings[boxID] = statsIDs[v];
saveSettings();
},

1
apps/sched/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

89
apps/sched/README.md Normal file
View File

@ -0,0 +1,89 @@
Sched: Scheduling library for alarms and timers
====================================
This provides boot code, a library and tools for alarms and timers.
Other apps can use this to provide alarm functionality.
App
---
The Alarm app allows you to add/modify any running timers.
Internals / Library
-------------------
Alarms are stored in an array in `sched.json`, and take the form:
```
{
id : "mytimer", // optional ID for this alarm/timer, so apps can easily find *their* timers
appid : "myappid", // optional app ID for alarms that you set/use for your app
on : true, // is the alarm enabled?
t : 23400000, // Time of day since midnight in ms (if a timer, this is set automatically when timer starts)
dow : 0b1111111, // Binary encoding for days of the week to run alarm on
// SUN = 1
// MON = 2
// TUE = 4
// WED = 8
// THU = 16
// FRI = 32
// SAT = 64
date : "2022-04-04", // OPTIONAL date for the alarm, in YYYY-MM-DD format
// eg (new Date()).toISOString().substr(0,10)
msg : "Eat food", // message to display.
last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day!
rp : true, // repeat the alarm every day?
vibrate : "...", // OPTIONAL pattern of '.', '-' and ' ' to use for when buzzing out this alarm (defaults to '..' if not set)
hidden : false, // OPTIONAL if false, the widget should not show an icon for this alarm
as : false, // auto snooze
timer : 5*60*1000, // OPTIONAL - if set, this is a timer and it's the time in ms
js : "load('myapp.js')" // OPTIONAL - a JS command to execute when the alarm activates (*instead* of loading 'sched.js')
// when this code is run, you're responsible for setting alarm.on=false (or removing the alarm)
data : { ... } // OPTIONAL - your app can store custom data in here if needed (don't store a lot of data here)
}
```
The [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched/lib.js) contains
a few helpful functions for getting/setting alarms and timers, but is intentionally sparse so as not to
use too much RAM.
It can be used as follows:
```
// add/update an existing alarm
require("sched").setAlarm("mytimer", {
msg : "Wake up",
timer : 10*60*1000, // 10 Minutes
});
// Ensure the widget and alarm timer updates to schedule the new alarm properly
require("sched").reload();
// Get the time to the next alarm for us
var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer"));
// timeToNext===undefined if no alarm or alarm disabled
// delete an alarm
require("sched").setAlarm("mytimer", undefined);
// reload after deleting...
require("sched").reload();
// Or add an alarm that runs your own code - in this case
// loading the settings app. The alarm will not be removed/stopped
// automatically.
require("sched").setAlarm("customrunner", {
appid : "myapp",
js : "load('setting.app.js')",
timer : 1*60*1000, // 1 Minute
});
// If you have been specifying `appid` you can also find any alarms that
// your app has created with the following:
require("sched").getAlarms().filter(a=>a.appid=="myapp");
```
If your app requires alarms, you can specify that the alarms app needs to
be installed by adding `"dependencies": {"scheduler":"type"},` to your app's
metadata.

1
apps/sched/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkGswAhiMRCCAREAo4eHBIQLEAgwYHsIJDiwHB5gACBpIhHCoYZEGA4gFCw4ABGA4HEjgXJ4IXGAwcUB4VEmf//8zogICoJIFAodMBoNDCoIADmgJB4gXIFwXDCwoABngwFC4guB4k/CQXwh4EC+YMCC44iBp4qDC4n/+gNBC41sEIJCEC4v/GAPGC4dhXYRdFC4xhCCYIXCdQRdDC5HzegQXCsxGHC45IDCwQXCUgwXHJAIXGRogXJSIIXcOw4XIPAYXcBwv/mEDBAwXOgtQC65QGC5vzoEAJAx3Nmk/mEABIiPN+dDAQIwFC4zXGFwKRCGAjvMFwQECGAgXI4YuGGAUvAgU8C4/EFwwGCAgdMC4p4EFwobFOwoXDJAIoEAApGBC4xIEABJGHGAapEAAqNBFwwXD4heI+YuBC5BIBVQhdHIw4wD5inFS4IKCCxFmigNCokzCoMzogICoIWIsMRjgPCAA3BiMWC48RBQIXJEgMRFxAJCCw4lEC44IECooOIBAaBJKwhgIAH4ACA=="))

BIN
apps/sched/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

25
apps/sched/boot.js Normal file
View File

@ -0,0 +1,25 @@
// check for alarms
(function() { // run in closure to ensure allocated vars get removed
if (Bangle.SCHED) {
clearTimeout(Bangle.SCHED);
delete Bangle.SCHED;
}
var alarms = require('Storage').readJSON('sched.json',1)||[];
var time = new Date();
var active = alarms.filter(a=>a.on && (a.dow>>time.getDay())&1 && (!a.date || a.date==time.toISOString().substr(0,10)));
if (active.length) {
active = active.sort((a,b)=>(a.t-b.t)+(a.last-b.last)*86400000);
var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000);
var t = active[0].t-currentTime;
if (active[0].last == time.getDate() || t < -60000) t += 86400000;
if (t<1000) t=1000; // start alarm min 1 sec from now
/* execute alarm at the correct time. We avoid execing immediately
since this code will get called AGAIN when alarm.js is loaded. alarm.js
will then clearInterval() to get rid of this call so it can proceed
normally.
If active[0].js is defined, just run that code as-is and not alarm.js */
Bangle.SCHED = setTimeout(active[0].js||'load("sched.js")',t);
} else { // check for new alarms at midnight (so day of week works)
Bangle.SCHED = setTimeout('eval(require("Storage").read("sched.boot.js"))', 86400000 - (Date.now()%86400000));
}
})();

54
apps/sched/lib.js Normal file
View File

@ -0,0 +1,54 @@
// Return an array of all alarms
exports.getAlarms = function() {
return require("Storage").readJSON("sched.json",1)||[];
};
// Write a list of alarms back to storage
exports.setAlarms = function(alarms) {
return require("Storage").writeJSON("sched.json",alarms);
};
// Return an alarm object based on ID
exports.getAlarm = function(id) {
return exports.getAlarms().find(a=>a.id==id);
};
// Given a list of alarms from getAlarms, return a list of active alarms for the given time (or current time if time not specified)
exports.getActiveAlarms = function(alarms, time) {
if (!time) time = new Date();
var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000)
+10000;// get current time - 10s in future to ensure we alarm if we've started the app a tad early
return alarms.filter(a=>a.on&&(a.t<currentTime)&&(a.last!=time.getDate()) && (!a.date || a.date==time.toISOString().substr(0,10))).sort((a,b)=>a.t-b.t);
}
// Set an alarm object based on ID. Leave 'alarm' undefined to remove it
exports.setAlarm = function(id, alarm) {
var alarms = exports.getAlarms().filter(a=>a.id!=id);
if (alarm !== undefined) {
alarm.id = id;
if (alarm.dow===undefined) alarm.dow = 0b1111111;
if (alarm.on!==false) alarm.on=true;
if (alarm.timer) { // if it's a timer, set the start time as a time from *now*
var time = new Date();
var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000);
alarm.t = currentTime + alarm.timer;
}
alarms.push(alarm);
}
exports.setAlarms(alarms);
};
/// Get time until the given alarm (object). Return undefined if alarm not enabled, or if 86400000 or more, alarm could me *more* than a day in the future
exports.getTimeToAlarm = function(alarm, time) {
if (!alarm) return undefined;
if (!time) time = new Date();
var active = alarm.on && (alarm.dow>>time.getDay())&1 && (!alarm.date || alarm.date==time.toISOString().substr(0,10));
if (!active) return undefined;
var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000);
var t = alarm.t-currentTime;
if (alarm.last == time.getDate() || t < -60000) t += 86400000;
return t;
};
/// Force a reload of the current alarms and widget
exports.reload = function() {
eval(require("Storage").read("sched.boot.js"));
if (WIDGETS["alarm"]) {
WIDGETS["alarm"].reload();
Bangle.drawWidgets();
}
};

Some files were not shown because too many files have changed in this diff Show More