Merge remote-tracking branch 'upstream/master'
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: New App!
|
||||||
|
0.02: Fullscreen settings.
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
# 90s Clock
|
||||||
|
|
||||||
|
A watch face in 90s style:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Fullscreen mode can be enabled in the settings:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
- [David Peer](https://github.com/peerdavid)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwgc8+fAgEgwAMDvPnz99BYdl2weHtu27ft2AGBiEcuEAhAPDg4jGgECIRMN23fthUNgP374vBAB3gAgc/gAXNjlx4EDxwJEpAjG/6IBjkBL4UAjVgBAJuCgPHBQMFEIkkyQjFhwEClgXBEYNBwkQJoibCBwNFBAUCEAVAQZAjC/8euPHDon//hKB//xEYMP//jBYP/+ARDNYM///+EYIgBj1B/8fCIUhEYQRB//FUIM/EZU4EYMkEYP/8VhEYUH/gRBWAUfI4MD+AjBoAsBwEH8EB/EDwE4HwYjCuEHWAOHgExEYKbBCIZNB8fAEYQHByE/EwPABAY+BgRHDBANyJQXHNwIjD8CSBj/+BwMSTwOOBYK2D/4CCNYZQB/iJBQwYjCCIcAgeBSoOAWYQjEVoIRCNAIjKAQKJBgAFC8ZoCWwJbDABMHGQPAAoMQB5EDx/4A4gqBZwIGCWwIABuBWC4EBZwPgv/AcwS/EAAcIU4IRBVQIRKEwIjBv0ARIUDCJIjD//x/ARK/5HC/+BCJkcI45uDgECUgQjCWAM4WwUBWYanEAA8cTARWBEYUC5RAHw1YgEOFQXADQPHIIkAhgICuARBh0A23blhHBagIKBsOGjNswhHDEYUUAoTUBhkxEYMwKwU503bvuwXILmCEYMYsumWYYjB85lDEYovBEYXm7fs25EBI4kYtOWNwIjD4+8NYsw4YjGz9/2hrEoOGjVBwE4NYdzNYSwBuEDEYcxaIUA8+atugGogjBiVgWAI"))
|
||||||
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"id": "90sclk",
|
||||||
|
"name": "90s Clock",
|
||||||
|
"version": "0.02",
|
||||||
|
"description": "A 90s style watch-face",
|
||||||
|
"readme": "README.md",
|
||||||
|
"icon": "app.png",
|
||||||
|
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_2.png"}],
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"allow_emulator": true,
|
||||||
|
"storage": [
|
||||||
|
{"name":"90sclk.app.js","url":"app.js"},
|
||||||
|
{"name":"90sclk.img","url":"app-icon.js","evaluate":true},
|
||||||
|
{"name":"90sclk.settings.js","url":"settings.js"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,31 @@
|
||||||
|
(function(back) {
|
||||||
|
const SETTINGS_FILE = "90sclk.setting.json";
|
||||||
|
|
||||||
|
// initialize with default settings...
|
||||||
|
const storage = require('Storage')
|
||||||
|
let settings = {
|
||||||
|
fullscreen: false,
|
||||||
|
};
|
||||||
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
|
for (const key in saved_settings) {
|
||||||
|
settings[key] = saved_settings[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
storage.write(SETTINGS_FILE, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
E.showMenu({
|
||||||
|
'': { 'title': '90s Clock' },
|
||||||
|
'< Back': back,
|
||||||
|
'Full Screen': {
|
||||||
|
value: settings.fullscreen,
|
||||||
|
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||||
|
onchange: () => {
|
||||||
|
settings.fullscreen = !settings.fullscreen;
|
||||||
|
save();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
@ -1,12 +1,34 @@
|
||||||
// place your const, vars, functions or classes here
|
// place your const, vars, functions or classes here
|
||||||
|
|
||||||
// special function to handle display switch on
|
// clear the screen
|
||||||
Bangle.on('lcdPower', (on) => {
|
g.clear();
|
||||||
if (on) {
|
|
||||||
// call your app function here
|
var n = 0;
|
||||||
// If you clear the screen, do Bangle.drawWidgets();
|
|
||||||
|
// 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();
|
// First draw...
|
||||||
// call your app function here
|
draw();
|
||||||
|
|
||||||
|
// Load widgets
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"aclock.app.js","url":"clock-analog.js"},
|
{"name":"aclock.app.js","url":"clock-analog.js"},
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,7 @@
|
||||||
0.13: Alarm widget state now updates when setting/resetting an alarm
|
0.13: Alarm widget state now updates when setting/resetting an alarm
|
||||||
0.14: Order of 'back' menu item
|
0.14: Order of 'back' menu item
|
||||||
0.15: Fix hour/minute wrapping code for new menu system
|
0.15: Fix hour/minute wrapping code for new menu system
|
||||||
|
0.16: Adding alarm library
|
||||||
|
0.17: Moving alarm internals to 'sched' library
|
||||||
|
0.18: Cope with >1 identical alarm at once (#1667)
|
||||||
|
0.19: Ensure rescheduled alarms that already fired have 'last' reset
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +1,43 @@
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
var alarms = require("Storage").readJSON("alarm.json",1)||[];
|
var alarms = require("sched").getAlarms();
|
||||||
/*alarms = [
|
// An array of alarm objects (see sched/README.md)
|
||||||
{ on : true,
|
|
||||||
hr : 6.5, // hours + minutes/60
|
// time in ms -> { hrs, mins }
|
||||||
msg : "Eat chocolate",
|
function decodeTime(t) {
|
||||||
last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day!
|
t = 0|t; // sanitise
|
||||||
rp : true, // repeat
|
var hrs = 0|(t/3600000);
|
||||||
as : false, // auto snooze
|
return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) };
|
||||||
timer : 5, // OPTIONAL - if set, this is a timer and it's the time in minutes
|
}
|
||||||
}
|
|
||||||
];*/
|
// time in { hrs, mins } -> ms
|
||||||
|
function encodeTime(o) {
|
||||||
|
return o.hrs*3600000 + o.mins*60000;
|
||||||
|
}
|
||||||
|
|
||||||
function formatTime(t) {
|
function formatTime(t) {
|
||||||
var hrs = 0|t;
|
var o = decodeTime(t);
|
||||||
var mins = Math.round((t-hrs)*60);
|
return o.hrs+":"+("0"+o.mins).substr(-2);
|
||||||
return hrs+":"+("0"+mins).substr(-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMins(t) {
|
function getCurrentTime() {
|
||||||
mins = (0|t)%60;
|
|
||||||
hrs = 0|(t/60);
|
|
||||||
return hrs+":"+("0"+mins).substr(-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentHr() {
|
|
||||||
var time = new Date();
|
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() {
|
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 = {
|
const menu = {
|
||||||
'': { 'title': 'Alarm/Timer' },
|
'': { 'title': 'Alarm/Timer' },
|
||||||
/*LANG*/'< Back' : ()=>{load();},
|
/*LANG*/'< Back' : ()=>{load();},
|
||||||
|
|
@ -38,141 +45,161 @@ function showMainMenu() {
|
||||||
/*LANG*/'New Timer': ()=>editTimer(-1)
|
/*LANG*/'New Timer': ()=>editTimer(-1)
|
||||||
};
|
};
|
||||||
alarms.forEach((alarm,idx)=>{
|
alarms.forEach((alarm,idx)=>{
|
||||||
|
var type,txt; // a leading space is currently required (JS error in Espruino 2v12)
|
||||||
if (alarm.timer) {
|
if (alarm.timer) {
|
||||||
txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer);
|
type = /*LANG*/"Timer";
|
||||||
|
txt = " "+formatTime(alarm.timer);
|
||||||
} else {
|
} else {
|
||||||
txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr);
|
type = /*LANG*/"Alarm";
|
||||||
if (alarm.rp) txt += /*LANG*/" (repeat)";
|
txt = " "+formatTime(alarm.t);
|
||||||
}
|
}
|
||||||
menu[txt] = function() {
|
if (alarm.rp) txt += "\0"+atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA=");
|
||||||
if (alarm.timer) editTimer(idx);
|
// rename duplicate alarms
|
||||||
else editAlarm(idx);
|
if (menu[type+txt]) {
|
||||||
|
var n = 2;
|
||||||
|
while (menu[type+" "+n+txt]) n++;
|
||||||
|
txt = type+" "+n+txt;
|
||||||
|
} else txt = type+txt;
|
||||||
|
// add to menu
|
||||||
|
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();
|
if (WIDGETS["alarm"]) WIDGETS["alarm"].reload();
|
||||||
return E.showMenu(menu);
|
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 newAlarm = alarmIndex<0;
|
||||||
var hrs = 12;
|
var a = {
|
||||||
var mins = 0;
|
t : 12*3600000, // 12 o clock default
|
||||||
var en = true;
|
on : true,
|
||||||
var repeat = true;
|
rp : false, // repeat not the default
|
||||||
var as = false;
|
as : false,
|
||||||
if (!newAlarm) {
|
dow : 0b1111111,
|
||||||
var a = alarms[alarmIndex];
|
last : 0,
|
||||||
hrs = 0|a.hr;
|
vibrate : ".."
|
||||||
mins = Math.round((a.hr-hrs)*60);
|
|
||||||
en = a.on;
|
|
||||||
repeat = a.rp;
|
|
||||||
as = a.as;
|
|
||||||
}
|
}
|
||||||
|
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||||
|
if (alarm) Object.assign(a,alarm);
|
||||||
|
var t = decodeTime(a.t);
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': /*LANG*/'Alarm' },
|
'': { 'title': /*LANG*/'Alarm' },
|
||||||
/*LANG*/'< Back' : showMainMenu,
|
'< Back' : () => showMainMenu(),
|
||||||
/*LANG*/'Hours': {
|
/*LANG*/'Hours': {
|
||||||
value: hrs, min : 0, max : 23, wrap : true,
|
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||||
onchange: v => hrs=v
|
onchange: v => t.hrs=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Minutes': {
|
/*LANG*/'Minutes': {
|
||||||
value: mins, min : 0, max : 59, wrap : true,
|
value: t.mins, min : 0, max : 59, wrap : true,
|
||||||
onchange: v => mins=v
|
onchange: v => t.mins=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Enabled': {
|
/*LANG*/'Enabled': {
|
||||||
value: en,
|
value: a.on,
|
||||||
format: v=>v?"On":"Off",
|
format: v=>v?"On":"Off",
|
||||||
onchange: v=>en=v
|
onchange: v=>a.on=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Repeat': {
|
/*LANG*/'Repeat': {
|
||||||
value: en,
|
value: a.rp,
|
||||||
format: v=>v?"Yes":"No",
|
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': {
|
/*LANG*/'Auto snooze': {
|
||||||
value: as,
|
value: a.as,
|
||||||
format: v=>v?"Yes":"No",
|
format: v=>v?"Yes":"No",
|
||||||
onchange: v=>as=v
|
onchange: v=>a.as=v
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function getAlarm() {
|
menu[/*LANG*/"Save"] = function() {
|
||||||
var hr = hrs+(mins/60);
|
a.t = encodeTime(t);
|
||||||
var day = 0;
|
a.last = (a.t < getCurrentTime()) ? (new Date()).getDate() : 0;
|
||||||
// If alarm is for tomorrow not today (eg, in the past), set day
|
if (newAlarm) alarms.push(a);
|
||||||
if (hr < getCurrentHr())
|
else alarms[alarmIndex] = a;
|
||||||
day = (new Date()).getDate();
|
saveAndReload();
|
||||||
// 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));
|
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
if (!newAlarm) {
|
if (!newAlarm) {
|
||||||
menu[/*LANG*/"> Delete"] = function() {
|
menu[/*LANG*/"Delete"] = function() {
|
||||||
alarms.splice(alarmIndex,1);
|
alarms.splice(alarmIndex,1);
|
||||||
require("Storage").write("alarm.json",JSON.stringify(alarms));
|
saveAndReload();
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return E.showMenu(menu);
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editTimer(alarmIndex) {
|
function editTimer(alarmIndex, alarm) {
|
||||||
var newAlarm = alarmIndex<0;
|
var newAlarm = alarmIndex<0;
|
||||||
var hrs = 0;
|
var a = {
|
||||||
var mins = 5;
|
timer : 5*60*1000, // 5 minutes
|
||||||
var en = true;
|
on : true,
|
||||||
if (!newAlarm) {
|
rp : false,
|
||||||
var a = alarms[alarmIndex];
|
as : false,
|
||||||
mins = (0|a.timer)%60;
|
dow : 0b1111111,
|
||||||
hrs = 0|(a.timer/60);
|
last : 0,
|
||||||
en = a.on;
|
vibrate : ".."
|
||||||
}
|
}
|
||||||
|
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||||
|
if (alarm) Object.assign(a,alarm);
|
||||||
|
var t = decodeTime(a.timer);
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': /*LANG*/'Timer' },
|
'': { 'title': /*LANG*/'Timer' },
|
||||||
/*LANG*/'< Back' : showMainMenu,
|
'< Back' : () => showMainMenu(),
|
||||||
/*LANG*/'Hours': {
|
/*LANG*/'Hours': {
|
||||||
value: hrs, min : 0, max : 23, wrap : true,
|
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||||
onchange: v => hrs=v
|
onchange: v => t.hrs=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Minutes': {
|
/*LANG*/'Minutes': {
|
||||||
value: mins, min : 0, max : 59, wrap : true,
|
value: t.mins, min : 0, max : 59, wrap : true,
|
||||||
onchange: v => mins=v
|
onchange: v => t.mins=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Enabled': {
|
/*LANG*/'Enabled': {
|
||||||
value: en,
|
value: a.on,
|
||||||
format: v=>v?/*LANG*/"On":/*LANG*/"Off",
|
format: v=>v?"On":"Off",
|
||||||
onchange: v=>en=v
|
onchange: v=>a.on=v
|
||||||
}
|
},
|
||||||
|
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
|
||||||
};
|
};
|
||||||
function getTimer() {
|
menu[/*LANG*/"Save"] = function() {
|
||||||
var d = new Date(Date.now() + ((hrs*60)+mins)*60000);
|
a.timer = encodeTime(t);
|
||||||
var hr = d.getHours() + (d.getMinutes()/60) + (d.getSeconds()/3600);
|
a.t = getCurrentTime() + a.timer;
|
||||||
// Save alarm
|
a.last = 0;
|
||||||
return {
|
if (newAlarm) alarms.push(a);
|
||||||
on : en,
|
else alarms[alarmIndex] = a;
|
||||||
timer : (hrs*60)+mins,
|
saveAndReload();
|
||||||
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));
|
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
if (!newAlarm) {
|
if (!newAlarm) {
|
||||||
menu["> Delete"] = function() {
|
menu[/*LANG*/"Delete"] = function() {
|
||||||
alarms.splice(alarmIndex,1);
|
alarms.splice(alarmIndex,1);
|
||||||
require("Storage").write("alarm.json",JSON.stringify(alarms));
|
saveAndReload();
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
{
|
{
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Default Alarm & Timer",
|
"name": "Alarm & Timer",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.15",
|
"version": "0.19",
|
||||||
"description": "Set and respond to alarms and timers",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"dependencies": {"scheduler":"type"},
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"alarm.app.js","url":"app.js"},
|
{"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.img","url":"app-icon.js","evaluate":true},
|
||||||
{"name":"alarm.wid.js","url":"widget.js"}
|
{"name":"alarm.wid.js","url":"widget.js"}
|
||||||
],
|
]
|
||||||
"data": [{"name":"alarm.json"}]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
WIDGETS["alarm"]={area:"tl",width:0,draw:function() {
|
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);
|
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() {
|
},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();
|
WIDGETS["alarm"].reload();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: New App!
|
||||||
|
0.02: Actually upload correct code
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEw4UA///t9TmuV3+GJf4AN+ALVgf8BasP/4LVn//4ALUWgJUJBZUDBYJUIBZcP3/nKhEOt/WBZE5r+VKg0KgEVr9V3wLHqtaqt9sALElWAqoABt1QBZNeBYuq0ILCrVUBYulBYVWBYkCBYgABBZ8K1WVBYlABZegKQWqBQlVqALKqWoKQWpBYtWBZeqKRAAB1WABZZSHAANq0ALLKQ6qC1ALLKQ5UEAH4AG"))
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
Bangle.setBarometerPower(true, "app");
|
||||||
|
|
||||||
|
g.clear(1);
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
var zero = 0;
|
||||||
|
var R = Bangle.appRect;
|
||||||
|
var y = R.y + R.h/2;
|
||||||
|
var MEDIANLENGTH = 20;
|
||||||
|
var avr = [], median;
|
||||||
|
var value = 0;
|
||||||
|
|
||||||
|
Bangle.on('pressure', function(e) {
|
||||||
|
while (avr.length>MEDIANLENGTH) avr.pop();
|
||||||
|
avr.unshift(e.altitude);
|
||||||
|
median = avr.slice().sort();
|
||||||
|
g.reset().clearRect(0,y-30,g.getWidth()-10,y+30);
|
||||||
|
if (median.length>10) {
|
||||||
|
var mid = median.length>>1;
|
||||||
|
value = E.sum(median.slice(mid-4,mid+5)) / 9;
|
||||||
|
g.setFont("Vector",50).setFontAlign(0,0).drawString((value-zero).toFixed(1), g.getWidth()/2, y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
g.reset();
|
||||||
|
g.setFont("6x8").setFontAlign(0,0).drawString(/*LANG*/"ALTITUDE (m)", g.getWidth()/2, y-40);
|
||||||
|
g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"ZERO", g.getWidth()-5, g.getHeight()/2);
|
||||||
|
setWatch(function() {
|
||||||
|
zero = value;
|
||||||
|
}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true});
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,12 @@
|
||||||
|
{ "id": "altimeter",
|
||||||
|
"name": "Altimeter",
|
||||||
|
"version":"0.02",
|
||||||
|
"description": "Simple altimeter that can display height changed using Bangle.js 2's built in pressure sensor.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"tags": "tool,outdoors",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"altimeter.app.js","url":"app.js"},
|
||||||
|
{"name":"altimeter.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
0.01: New app!
|
||||||
|
0.02: Fix bug with regenerating index, fix bug in word lookups
|
||||||
|
0.03: Improve word search performance
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
# 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).
|
||||||
|
This file can be replaced with a custom dictionary, an ASCII file containing a newline-separated (single "\n", not DOS-style "\r\n") alphabetically
|
||||||
|
sorted (sorting is important for the word lookup algorithm) list of words.
|
||||||
|
|
||||||
|

|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AE2JAAIKHnc7DyNPp4vRGAwuBGB4sBAAQvSGIovPFqYvHGAYvDGBYsGGhwvGGIQvEGBQnDMYhkNGBAvOvQABqyRTF5GJr4wLFwQACX6IwLsowJLYMrldVGAQvTsoADGBITD0YvDldPF6n+F4gyGGAdP5nMF4KKBGDJZDGI7EBcoOiGAK7DGAQvYRogxEr1Pp9VMAiSBBILBWeJIxCromBMAQwDAAZfTGBQyCxOCGAIvBGIV/F7AwMAAOIp95GAYACFqoyQMAIwGF7QADEQd5FgIADqvGF8DnEAAIvFGIWjF8CFE0QwHAAQudAAK0EGBQuecw3GqpemYIxiCGIa8cF4wwHdTwvJp9/F82jGA9VMQovf5jkHGIwvg4wvIAAgvg5miF9wwNF8QABF9QwF0YuoF4oxCqoulGBAAB42i0QvjGBPMF0gwIFswwHF1IA/AH4A/AH4AL"))
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,181 @@
|
||||||
|
|
||||||
|
const S = require("Storage");
|
||||||
|
const words = S.read("bee.words");
|
||||||
|
var letters = [];
|
||||||
|
|
||||||
|
var centers = [];
|
||||||
|
|
||||||
|
var word = '';
|
||||||
|
|
||||||
|
var foundWords = [];
|
||||||
|
var score = 0;
|
||||||
|
|
||||||
|
var intervalID = -1;
|
||||||
|
|
||||||
|
function biSearch(w, ws, start, end, count) {
|
||||||
|
"compile"
|
||||||
|
if (start>end-w.legnth || count--<=0) return ws.substr(start, end-start).indexOf("\n"+w+"\n");
|
||||||
|
var mid = (end+start)>>1;
|
||||||
|
if (ws[mid-1]==="\n") --mid;
|
||||||
|
else while (mid<end && ws[mid]!=="\n") mid++;
|
||||||
|
var i = 0;
|
||||||
|
while (i<w.length && ws[mid+i+1]==w[i]) ++i;
|
||||||
|
if (i==w.length && ws[mid+i+1]==="\n") return mid+1;
|
||||||
|
if (i==w.length || w[i]<ws[mid+i+1]) return biSearch(w, ws, start, mid+1, count);
|
||||||
|
if (w[i]>ws[mid+i+1]) return biSearch(w, ws, mid+1, end, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (biSearch(w, words, 0, words.length, 20)>-1) {
|
||||||
|
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 i = Math.floor((words.length-10)*Math.random());
|
||||||
|
while (words[i]!="\n" && i<words.length) ++i;
|
||||||
|
if (i<words.length-1) {
|
||||||
|
++i;
|
||||||
|
while (words[i]!=="\n") {
|
||||||
|
var c = words[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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pickLetters();
|
||||||
|
drawHive();
|
||||||
|
drawScore();
|
||||||
|
Bangle.on("touch", touchHandler);
|
||||||
|
Bangle.on("swipe", swipeHandler);
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,15 @@
|
||||||
|
{ "id": "bee",
|
||||||
|
"name": "Bee",
|
||||||
|
"shortName":"Bee",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version":"0.03",
|
||||||
|
"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.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"screenshots": [{"url":"berlin-clock-screenshot.png"}],
|
"screenshots": [{"url":"berlin-clock-screenshot.png"}],
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: New App
|
||||||
|
0.02: app keeps track of statistics now
|
||||||
|
|
@ -21,7 +21,8 @@ function buttonPushed(b) {
|
||||||
layout.bt1.bgCol = wordle.keyColors.Z||g.theme.bg;
|
layout.bt1.bgCol = wordle.keyColors.Z||g.theme.bg;
|
||||||
layout.bt2.label = "<del>";
|
layout.bt2.label = "<del>";
|
||||||
layout.bt4.label = "<ent>";
|
layout.bt4.label = "<ent>";
|
||||||
layout.bt3.label = layout.bt5.label = " ";
|
layout.bt3.label = " ";
|
||||||
|
layout.bt5.label = "<stat>";
|
||||||
layout.bt6.label = "<";
|
layout.bt6.label = "<";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,6 +31,10 @@ function buttonPushed(b) {
|
||||||
if (b!=6) {
|
if (b!=6) {
|
||||||
if ((keyStateIdx<=5 || b<=1) && inp.length<5) inp += String.fromCharCode(b+(keyStateIdx-1)*5+64);
|
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);
|
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.input.label = inp;
|
||||||
}
|
}
|
||||||
layout = getKeyLayout(inp);
|
layout = getKeyLayout(inp);
|
||||||
|
|
@ -82,6 +87,7 @@ class Wordle {
|
||||||
this.word = this.words.slice(i, i+5).toUpperCase();
|
this.word = this.words.slice(i, i+5).toUpperCase();
|
||||||
}
|
}
|
||||||
console.log(this.word);
|
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) {
|
render(clear) {
|
||||||
h = g.getHeight();
|
h = g.getHeight();
|
||||||
|
|
@ -109,7 +115,7 @@ class Wordle {
|
||||||
layout = getKeyLayout("");
|
layout = getKeyLayout("");
|
||||||
wordle.render(true);
|
wordle.render(true);
|
||||||
});
|
});
|
||||||
return 3;
|
return 1;
|
||||||
}
|
}
|
||||||
this.guesses.push(w);
|
this.guesses.push(w);
|
||||||
this.nGuesses++;
|
this.nGuesses++;
|
||||||
|
|
@ -130,13 +136,39 @@ class Wordle {
|
||||||
this.guessColors[this.nGuesses].push(col);
|
this.guessColors[this.nGuesses].push(col);
|
||||||
}
|
}
|
||||||
if (correct==5) {
|
if (correct==5) {
|
||||||
E.showAlert("The word is\n"+this.word, "You won in "+(this.nGuesses+1)+" guesses!").then(function(){load();});
|
E.showAlert("The word is\n"+this.word, "You won in "+(this.nGuesses+1)+" guesses!").then(function(){
|
||||||
return 1;
|
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'];
|
||||||
if (this.nGuesses==5) {
|
require("Storage").writeJSON("bordlestats.json", wordle.stats);
|
||||||
E.showAlert("The word was\n"+this.word, "You lost!").then(function(){load();});
|
wordle.drawStats();
|
||||||
|
});
|
||||||
return 2;
|
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(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "Bordle",
|
"name": "Bordle",
|
||||||
"shortName":"Bordle",
|
"shortName":"Bordle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Bangle version of a popular word search game",
|
"description": "Bangle version of a popular word search game",
|
||||||
"supports" : ["BANGLEJS2"],
|
"supports" : ["BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
0.01: New App.
|
||||||
|
0.02: Use build in function for steps and other improvements.
|
||||||
|
0.03: Adapt colors based on the theme of the user.
|
||||||
|
0.04: Steps can be hidden now such that the time is even larger.
|
||||||
|
0.05: Included icons for information.
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Black & White clock
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Fullscreen on/off
|
||||||
|
- The design is adapted to the theme of your bangle.
|
||||||
|
- Tab left/right of screen to show steps, temperature etc.
|
||||||
|
- Enable / disable lock icon in the settings.
|
||||||
|
- If the "sched" app is installed tab top / bottom of the screen to set the timer.
|
||||||
|
|
||||||
|
## Thanks to
|
||||||
|
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
- [David Peer](https://github.com/peerdavid)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwgIcah0EgEB/H8iFsAoOY4kMBYMDhmGgXkAoUGiWkAoQQBoAFCjgnCAoM4hgFDuEI+wpC8EKyg1C/0eAoMAsEAiQvBAAeAApQAB/4Ao+P4v/wn0P8Pgn/wnkH4Pjv/j/nn9PH//n/nj/IFF4F88AXBAoM88EcAoPHj//jlDAoOf/+Y+YFHjnnjAjBEIIjD+BHDO9IALA=="))
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"id": "bwclk",
|
||||||
|
"name": "BlackWhite Clock",
|
||||||
|
"version": "0.05",
|
||||||
|
"description": "Black and white clock.",
|
||||||
|
"readme": "README.md",
|
||||||
|
"icon": "app.png",
|
||||||
|
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}],
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"allow_emulator": true,
|
||||||
|
"storage": [
|
||||||
|
{"name":"bwclk.app.js","url":"app.js"},
|
||||||
|
{"name":"bwclk.img","url":"app-icon.js","evaluate":true},
|
||||||
|
{"name":"bwclk.settings.js","url":"settings.js"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1,40 @@
|
||||||
|
(function(back) {
|
||||||
|
const SETTINGS_FILE = "bwclk.setting.json";
|
||||||
|
|
||||||
|
// initialize with default settings...
|
||||||
|
const storage = require('Storage')
|
||||||
|
let settings = {
|
||||||
|
fullscreen: false,
|
||||||
|
showLock: true,
|
||||||
|
};
|
||||||
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
|
for (const key in saved_settings) {
|
||||||
|
settings[key] = saved_settings[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
storage.write(SETTINGS_FILE, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
E.showMenu({
|
||||||
|
'': { 'title': 'BlackWhite Clock' },
|
||||||
|
'< Back': back,
|
||||||
|
'Fullscreen': {
|
||||||
|
value: settings.fullscreen,
|
||||||
|
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||||
|
onchange: () => {
|
||||||
|
settings.fullscreen = !settings.fullscreen;
|
||||||
|
save();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Show Lock': {
|
||||||
|
value: settings.showLock,
|
||||||
|
format: () => (settings.showLock ? 'Yes' : 'No'),
|
||||||
|
onchange: () => {
|
||||||
|
settings.showLock = !settings.showLock;
|
||||||
|
save();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"screenshots": [{"url":"screenshot_calculator.png"}],
|
"screenshots": [{"url":"screenshot_calculator.png"}],
|
||||||
"tags": "app,tool",
|
"tags": "app,tool",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"calculator.app.js","url":"app.js"},
|
{"name":"calculator.app.js","url":"app.js"},
|
||||||
{"name":"calculator.img","url":"calculator-icon.js","evaluate":true}
|
{"name":"calculator.img","url":"calculator-icon.js","evaluate":true}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock,cli,command,bash,shell",
|
"tags": "clock,cli,command,bash,shell",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"cliock.app.js","url":"app.js"},
|
{"name":"cliock.app.js","url":"app.js"},
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@
|
||||||
0.03: Eliminate flickering
|
0.03: Eliminate flickering
|
||||||
0.04: Fix for Bangle.js 2 and themes
|
0.04: Fix for Bangle.js 2 and themes
|
||||||
0.05: Fix bearing not clearing correctly (visible in single or double digit bearings)
|
0.05: Fix bearing not clearing correctly (visible in single or double digit bearings)
|
||||||
|
0.06: Add button for force compass calibration
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Compass
|
||||||
|
|
||||||
|
This app uses Bangle.js's built-in magnetometer as a compass.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Hold your Bangle.js **face up** (so the display is parallel to the ground),
|
||||||
|
and the red arrow will point north, with the heading in degrees printed at
|
||||||
|
the top of the screen.
|
||||||
|
|
||||||
|
This compass app does not include tilt compensation - so much like a real
|
||||||
|
compass you should always keep it face up when taking a reading.
|
||||||
|
|
||||||
|
The first time you run the compass after your Bangle has booted (or if
|
||||||
|
you move to an area with a substantially different magnetic field) you will
|
||||||
|
need to recalibrate your compass (even if a heading is shown).
|
||||||
|
|
||||||
|
## Calibration
|
||||||
|
|
||||||
|
Press the button next to the `RESET` label on the screen. The North/South marker
|
||||||
|
will disappear and a message will appear asking you to rotate the watch 360 degrees.
|
||||||
|
|
||||||
|
* Hold the watch face up, so the display is parallel to the ground
|
||||||
|
* Rotate it around slowly, all 360 degrees (with the display still parallel to the ground)
|
||||||
|
* The `Uncalibrated` message will disappear before you have finished rotating the full 360 degrees - but you should still complete the full rotation in order for the compass to work properly.
|
||||||
|
|
||||||
|
Once you've rotated the full 360 degrees your compass should now work fine,
|
||||||
|
and calibration is stored between runs of the app. However if you go near
|
||||||
|
to a strong magnet you may still need to recalibrate.
|
||||||
|
|
@ -38,7 +38,7 @@ Bangle.on('mag', function(m) {
|
||||||
if (!wasUncalibrated) {
|
if (!wasUncalibrated) {
|
||||||
g.clearRect(0,24,W,48);
|
g.clearRect(0,24,W,48);
|
||||||
g.setFontAlign(0,-1).setFont("6x8");
|
g.setFontAlign(0,-1).setFont("6x8");
|
||||||
g.drawString("Uncalibrated\nturn 360° around",M,24+4);
|
g.drawString(/*LANG*/"Uncalibrated\nturn 360° around",M,24+4);
|
||||||
wasUncalibrated = true;
|
wasUncalibrated = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -64,7 +64,12 @@ Bangle.on('mag', function(m) {
|
||||||
oldHeading = m.heading;
|
oldHeading = m.heading;
|
||||||
});
|
});
|
||||||
|
|
||||||
g.clear();
|
g.clear(1);
|
||||||
|
g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"RESET", g.getWidth()-5, g.getHeight()/2);
|
||||||
|
setWatch(function() {
|
||||||
|
Bangle.resetCompass();
|
||||||
|
}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true});
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
Bangle.setCompassPower(1);
|
Bangle.setCompassPower(1);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
{
|
{
|
||||||
"id": "compass",
|
"id": "compass",
|
||||||
"name": "Compass",
|
"name": "Compass",
|
||||||
"version": "0.05",
|
"version": "0.06",
|
||||||
"description": "Simple compass that points North",
|
"description": "Simple compass that points North",
|
||||||
"icon": "compass.png",
|
"icon": "compass.png",
|
||||||
"screenshots": [{"url":"screenshot_compass.png"}],
|
"screenshots": [{"url":"screenshot_compass.png"}],
|
||||||
"tags": "tool,outdoors",
|
"tags": "tool,outdoors",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"compass.app.js","url":"compass.js"},
|
{"name":"compass.app.js","url":"compass.js"},
|
||||||
{"name":"compass.img","url":"compass-icon.js","evaluate":true}
|
{"name":"compass.img","url":"compass-icon.js","evaluate":true}
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@
|
||||||
0.03: fix metadata.json to allow setting as clock
|
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.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.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
|
||||||
|
|
|
||||||
|
|
@ -28,5 +28,6 @@ See [#1248](https://github.com/espruino/BangleApps/issues/1248)
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
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.
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,17 @@ Graphics.prototype.setFontRoboto20 = function(scale) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function assignPalettes() {
|
function assignPalettes() {
|
||||||
// palette for 0-40%
|
if (g.theme.dark) {
|
||||||
pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]);
|
// palette for 0-40%
|
||||||
// palette for 50-100%
|
pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]);
|
||||||
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), 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() {
|
function setSmallFont20() {
|
||||||
|
|
@ -109,10 +116,10 @@ function updateSunRiseSunSet(now, lat, lon, line){
|
||||||
const infoData = {
|
const infoData = {
|
||||||
ID_DATE: { calc: () => {var d = (new Date()).toString().split(" "); return d[2] + ' ' + d[1] + ' ' + d[3];} },
|
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_DAY: { calc: () => {var d = require("locale").dow(new Date()).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} },
|
||||||
ID_SR: { calc: () => 'Sunrise ' + sunRise },
|
ID_SR: { calc: () => 'SUNRISE ' + sunRise },
|
||||||
ID_SS: { calc: () => 'Sunset ' + sunSet },
|
ID_SS: { calc: () => 'SUNSET ' + sunSet },
|
||||||
ID_STEP: { calc: () => 'Steps ' + getSteps() },
|
ID_STEP: { calc: () => 'STEPS ' + getSteps() },
|
||||||
ID_BATT: { calc: () => 'Battery ' + E.getBattery() + '%' },
|
ID_BATT: { calc: () => 'BATTERY ' + E.getBattery() + '%' },
|
||||||
ID_HRM: { calc: () => hrmCurrent }
|
ID_HRM: { calc: () => hrmCurrent }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -225,7 +232,7 @@ function drawSteps() {
|
||||||
setSmallFont();
|
setSmallFont();
|
||||||
g.setFontAlign(0,0);
|
g.setFontAlign(0,0);
|
||||||
g.setColor(g.theme.fg);
|
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;
|
drawingSteps = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
{ "id": "daisy",
|
{ "id": "daisy",
|
||||||
"name": "Daisy",
|
"name": "Daisy",
|
||||||
"version":"0.05",
|
"version":"0.06",
|
||||||
"dependencies": {"mylocation":"app"},
|
"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",
|
"icon": "app.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports" : ["BANGLEJS2"],
|
"supports" : ["BANGLEJS2"],
|
||||||
"screenshots": [{"url":"screenshot_daisy2.jpg"}],
|
"screenshots": [{"url":"screenshot_daisy3.png"}],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"daisy.app.js","url":"app.js"},
|
{"name":"daisy.app.js","url":"app.js"},
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
|
@ -8,6 +8,7 @@
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"ffcniftyb.app.js","url":"app.js"},
|
{"name":"ffcniftyb.app.js","url":"app.js"},
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"floralclk.app.js","url":"app.js"},
|
{"name":"floralclk.app.js","url":"app.js"},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: First release
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Fuzzy Text Clock
|
||||||
|
|
||||||
|
An imprecise clock for when you're not in a rush.
|
||||||
|
|
||||||
|
This clock is a remake of one of my favourite Pebble watchfaces, Fuzzy Text International. I use this watch for weekends and holidays, when 'within 5 minutes of the actual time' is close enough!
|
||||||
|
|
||||||
|
By default it will use the language set on the watch, go to settings to pick:
|
||||||
|
* en_GB - English
|
||||||
|
* en_US - American
|
||||||
|
* es_ES - Spanish
|
||||||
|
* fr_FR - French
|
||||||
|
* no_NO - Norwegian
|
||||||
|
* sv_SE - Swedish
|
||||||
|
* de_DE - German
|
||||||
|
|
||||||
|
Most translations are taken from the original Fuzzy Text International code.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
* Bold hour word (as the pebble version has)
|
||||||
|
* Animation when changing time?
|
||||||
|
|
||||||
|
## References
|
||||||
|
Based on Pebble app Fuzzy Text International: https://github.com/hallettj/Fuzzy-Text-International
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
{
|
||||||
|
"en_GB":{
|
||||||
|
"hours":[
|
||||||
|
"midnight", "one", "two", "three", "four", "five",
|
||||||
|
"six", "seven", "eight", "nine", "ten", "eleven",
|
||||||
|
"twelve", "one", "two", "three", "four", "five",
|
||||||
|
"six", "seven", "eight", "nine", "ten", "eleven"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"*$1 o'clock",
|
||||||
|
"five past *$1",
|
||||||
|
"ten past *$1",
|
||||||
|
"quarter past *$1",
|
||||||
|
"twenty past *$1",
|
||||||
|
"twenty five past *$1",
|
||||||
|
"half past *$1",
|
||||||
|
"twenty five to *$2",
|
||||||
|
"twenty to *$2",
|
||||||
|
"quarter to *$2",
|
||||||
|
"ten to *$2",
|
||||||
|
"five to *$2"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
},
|
||||||
|
"en_US":{
|
||||||
|
"hours":[
|
||||||
|
"midnight", "one", "two", "three", "four", "five",
|
||||||
|
"six", "seven", "eight", "nine", "ten", "eleven",
|
||||||
|
"twelve", "one", "two", "three", "four", "five",
|
||||||
|
"six", "seven", "eight", "nine", "ten", "eleven"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"*$1 o'clock",
|
||||||
|
"five after *$1",
|
||||||
|
"ten after *$1",
|
||||||
|
"quarter after *$1",
|
||||||
|
"twenty after *$1",
|
||||||
|
"twenty five after *$1",
|
||||||
|
"half past *$1",
|
||||||
|
"twenty five to *$2",
|
||||||
|
"twenty to *$2",
|
||||||
|
"quarter to *$2",
|
||||||
|
"ten to *$2",
|
||||||
|
"five to *$2"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
},
|
||||||
|
"es_ES":{
|
||||||
|
"hours":[
|
||||||
|
"doce", "una", "dos", "tres", "cuatro", "cinco",
|
||||||
|
"seis", "siete", "ocho", "nueve", "diez", "once",
|
||||||
|
"doce", "una", "dos", "tres", "cuatro", "cinco",
|
||||||
|
"seis", "siete", "ocho", "nueve", "diez", "once"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"*$1 en punto",
|
||||||
|
"*$1 y cinco",
|
||||||
|
"*$1 y diez",
|
||||||
|
"*$1 y cuarto",
|
||||||
|
"*$1 y veinte",
|
||||||
|
"*$1 y veinti- cinco",
|
||||||
|
"*$1 y media",
|
||||||
|
"*$2 menos veinti- cinco",
|
||||||
|
"*$2 menos veinte",
|
||||||
|
"*$2 menos cuarto",
|
||||||
|
"*$2 menos diez",
|
||||||
|
"*$2 menos cinco"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
},
|
||||||
|
"fr_FR":{
|
||||||
|
"hours":[
|
||||||
|
"douze", "une", "deux", "trois", "quatre", "cinq",
|
||||||
|
"six", "sept", "huit", "neuf", "dix", "onze",
|
||||||
|
"douze", "une", "deux", "trois", "quatre", "cinq",
|
||||||
|
"six", "sept", "huit", "neuf", "dix", "onze"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"*$1 heures",
|
||||||
|
"*$1 heures cinq",
|
||||||
|
"*$1 heures dix",
|
||||||
|
"*$1 heures et quart",
|
||||||
|
"*$1 heures vingt",
|
||||||
|
"*$1 heures vingt- cinq",
|
||||||
|
"*$1 heures et demie",
|
||||||
|
"*$2 moins vingt- cinq",
|
||||||
|
"*$2 heures moins vingt",
|
||||||
|
"*$2 moins le quart",
|
||||||
|
"*$2 heures moins dix",
|
||||||
|
"*$2 heures moins cinq"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
},
|
||||||
|
"no_NB":{
|
||||||
|
"hours":[
|
||||||
|
"tolv", "ett", "to", "tre", "fire", "fem",
|
||||||
|
"seks", "sju", "åtte", "ni", "ti", "elleve",
|
||||||
|
"tolv", "ett", "to", "tre", "fire", "fem",
|
||||||
|
"seks", "sju", "åtte", "ni", "ti", "elleve"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"klokka er *$1",
|
||||||
|
"fem over *$1",
|
||||||
|
"ti over *$1",
|
||||||
|
"kvart over *$1",
|
||||||
|
"ti på halv *$2",
|
||||||
|
"fem på halv *$2",
|
||||||
|
"halv *$2",
|
||||||
|
"fem over halv *$2",
|
||||||
|
"ti over halv *$2",
|
||||||
|
"kvart på *$2",
|
||||||
|
"ti på *$2",
|
||||||
|
"fem på *$2"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
},
|
||||||
|
"nn_NO":{
|
||||||
|
"hours":[
|
||||||
|
"tolv", "eitt", "to", "tre", "fire", "fem",
|
||||||
|
"seks", "sju", "åtte", "ni", "ti", "elleve",
|
||||||
|
"tolv", "eitt", "to", "tre", "fire", "fem",
|
||||||
|
"seks", "sju", "åtte", "ni", "ti", "elleve"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"klokka er *$1",
|
||||||
|
"fem over *$1",
|
||||||
|
"ti over *$1",
|
||||||
|
"kvart over *$1",
|
||||||
|
"ti på halv *$2",
|
||||||
|
"fem på halv *$2",
|
||||||
|
"halv *$2",
|
||||||
|
"fem over halv *$2",
|
||||||
|
"ti over halv *$2",
|
||||||
|
"kvart på *$2",
|
||||||
|
"ti på *$2",
|
||||||
|
"fem på *$2"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
},
|
||||||
|
"sv_SE":{
|
||||||
|
"hours":[
|
||||||
|
"tolv", "ett", "två", "tre", "fyra", "fem",
|
||||||
|
"sex", "sju", "åtta", "nio", "tio", "elva",
|
||||||
|
"tolv", "ett", "två", "tre", "fyra", "fem",
|
||||||
|
"sex", "sju", "åtta", "nio", "tio", "elva"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"*$1",
|
||||||
|
"fem över *$1",
|
||||||
|
"tio över *$1",
|
||||||
|
"kvart över *$1",
|
||||||
|
"tjugo över *$1",
|
||||||
|
"fem i halv *$2",
|
||||||
|
"halv *$2",
|
||||||
|
"fem över halv *$2",
|
||||||
|
"tjugo i *$2",
|
||||||
|
"kvart i *$2",
|
||||||
|
"tio i *$2",
|
||||||
|
"fem i *$2"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
},
|
||||||
|
"de_DE":{
|
||||||
|
"hours":[
|
||||||
|
"zwölf", "eins", "zwei", "drei", "vier", "fünf",
|
||||||
|
"sechs", "sieben", "acht", "neun", "zehn", "elf",
|
||||||
|
"zwölf", "eins", "zwei", "drei", "vier", "fünf",
|
||||||
|
"sechs", "sieben", "acht", "neun", "zehn", "elf"
|
||||||
|
],
|
||||||
|
"minutes":[
|
||||||
|
"*$1 uhr",
|
||||||
|
"fünf nach *$1",
|
||||||
|
"zehn nach *$1",
|
||||||
|
"viertel nach *$1",
|
||||||
|
"zwanzig nach *$1",
|
||||||
|
"fünf for halb *$2",
|
||||||
|
"halb *$2",
|
||||||
|
"fünf nach halb *$2",
|
||||||
|
"zwanzig vor *$2",
|
||||||
|
"viertel vor *$2",
|
||||||
|
"zehn vor *$2",
|
||||||
|
"fünf vor *$2"
|
||||||
|
],
|
||||||
|
"text_scale":3.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -0,0 +1,75 @@
|
||||||
|
// adapted from https://github.com/hallettj/Fuzzy-Text-International/
|
||||||
|
const fuzzy_strings = require("Storage").readJSON("fuzzy_strings.json");
|
||||||
|
|
||||||
|
const SETTINGS_FILE = "fuzzyw.settings.json";
|
||||||
|
let settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'language': 'System', 'alignment':'Centre'};
|
||||||
|
|
||||||
|
if (settings.language == 'System') {
|
||||||
|
settings.language = require('locale').name;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fuzzy_string = fuzzy_strings[settings.language];
|
||||||
|
|
||||||
|
let timeout = 2.5*60;
|
||||||
|
let drawTimeout;
|
||||||
|
|
||||||
|
function queueDraw(seconds) {
|
||||||
|
let millisecs = seconds * 1000;
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
}, millisecs - (Date.now() % millisecs));
|
||||||
|
}
|
||||||
|
|
||||||
|
const h = g.getHeight();
|
||||||
|
const w = g.getWidth();
|
||||||
|
let align_mode = 0;
|
||||||
|
let align_pos = w/2;
|
||||||
|
if (settings.alignment =='Left') {
|
||||||
|
align_mode = -1;
|
||||||
|
align_pos = 0;
|
||||||
|
} else if (settings.alignment == 'Right') {
|
||||||
|
align_mode = 1;
|
||||||
|
align_pos = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeString(date) {
|
||||||
|
let segment = Math.round((date.getMinutes()*60 + date.getSeconds() + 1)/300);
|
||||||
|
let hour = date.getHours() + Math.floor(segment/12);
|
||||||
|
f_string = fuzzy_string.minutes[segment % 12];
|
||||||
|
if (f_string.includes('$1')) {
|
||||||
|
f_string = f_string.replace('$1', fuzzy_string.hours[(hour) % 24]);
|
||||||
|
} else {
|
||||||
|
f_string = f_string.replace('$2', fuzzy_string.hours[(hour + 1) % 24]);
|
||||||
|
}
|
||||||
|
return f_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
let time_string = getTimeString(new Date()).replace('*', '');
|
||||||
|
// print(time_string);
|
||||||
|
g.setFont('Vector', (h-24*2)/fuzzy_string.text_scale);
|
||||||
|
g.setFontAlign(align_mode, 0);
|
||||||
|
g.clearRect(0, 24, w, h-24);
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.drawString(g.wrapString(time_string, w).join("\n"), align_pos, h/2);
|
||||||
|
queueDraw(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
draw();
|
||||||
|
|
||||||
|
// Stop updates when LCD is off, restart when on
|
||||||
|
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.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwgP/ABX8oYFD+AFE8AFE8IXE8YFKwFCj08h4FBocenEHCIPDjk4CoIFBhlwAoeMuIFEuBSBAoOI+AFD4HxGoQFB+AFD4P4uYFC8P4gYFD/w7BAFEfApfEj+B/Ecg/Ah8A+EMg/Dw0YseHj/Dw/8sfHAoPH/lhDoIFBwFwj4FB40AvkPAoU8v4dCAoIdDw04FIMP4EOgFwh47Bj8EvEfw/DJwgFXABY"))
|
||||||
|
After Width: | Height: | Size: 893 B |
|
|
@ -0,0 +1,46 @@
|
||||||
|
(function(back) {
|
||||||
|
const SETTINGS_FILE = "fuzzyw.settings.json";
|
||||||
|
|
||||||
|
var align_options = ['Left','Centre','Right'];
|
||||||
|
var language_options = ['System', 'en_GB', 'en_US', 'es_ES', 'fr_FR', 'no_NO', 'sv_SE', 'de_DE'];
|
||||||
|
|
||||||
|
// initialize with default settings...
|
||||||
|
let s = {'language': 'System', 'alignment': 'Centre'};
|
||||||
|
|
||||||
|
// ...and overwrite them with any saved values
|
||||||
|
// This way saved values are preserved if a new version adds more settings
|
||||||
|
const storage = require('Storage')
|
||||||
|
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
|
||||||
|
const saved = settings || {}
|
||||||
|
for (const key in saved) {
|
||||||
|
s[key] = saved[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
settings = s
|
||||||
|
storage.write(SETTINGS_FILE, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu({
|
||||||
|
'': { 'title': 'Fuzzy Text Clock' },
|
||||||
|
'< Back': back,
|
||||||
|
'Language': {
|
||||||
|
value: 0 | language_options.indexOf(s.language),
|
||||||
|
min: 0, max: language_options.length - 1,
|
||||||
|
format: v => language_options[v],
|
||||||
|
onchange: v => {
|
||||||
|
s.language = language_options[v];
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Alignment': {
|
||||||
|
value: 0 | align_options.indexOf(s.alignment),
|
||||||
|
min: 0, max: align_options.length - 1,
|
||||||
|
format: v => align_options[v],
|
||||||
|
onchange: v => {
|
||||||
|
s.alignment = align_options[v];
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"id":"fuzzyw",
|
||||||
|
"name":"Fuzzy Text Clock",
|
||||||
|
"shortName": "Fuzzy Text",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "An imprecise clock for when you're not in a rush",
|
||||||
|
"readme": "README.md",
|
||||||
|
"icon":"fuzzyw.png",
|
||||||
|
"screenshots": [{"url":"fuzzyw-light.png"},{"url":"fuzzyw-dark.png"}],
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||||
|
"allow_emulator": true,
|
||||||
|
"storage": [
|
||||||
|
{"name":"fuzzyw.app.js","url":"fuzzyw.app.js"},
|
||||||
|
{"name":"fuzzyw.settings.js","url":"fuzzyw.settings.js"},
|
||||||
|
{"name":"fuzzyw.img","url":"fuzzyw.icon.js","evaluate":true},
|
||||||
|
{"name":"fuzzy_strings.json","url":"fuzzy_strings.json"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -6,3 +6,4 @@
|
||||||
0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw
|
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.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
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
# Play the game of 1024
|
# 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:
|
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
|
## 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 **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
|
||||||
- Button **\***: Change the text on the tile to number, capitals or Roman numbers
|
- You can set the maximum undo level in the Apps settings menu.
|
||||||
- Button **R**: Reset the game. The Higscore will be remembered. You will be prompted first.
|
|
||||||
|
- 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: 
|
||||||
|
|
||||||
|
- 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
|
### Credits
|
||||||
|
|
||||||
Game 1024 is based on Saming's 2048 and Misho M. Petkovic 1024game.org and conceptually similar to Threes by Asher Vollmer.
|
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:
|
In Dark theme with numbers:
|
||||||

|

|
||||||
|
|
||||||
In Light theme with characters:
|
In Light theme with characters:
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,20 @@
|
||||||
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 middle = {x:Math.floor(g.getWidth()/2)-20, y: Math.floor(g.getHeight()/2)};
|
||||||
const rows = 4, cols = 4;
|
const rows = 4, cols = 4; // #settings
|
||||||
const borderWidth = 6;
|
const borderWidth = 6;
|
||||||
const sqWidth = (Math.floor(Bangle.appRect.w - 48) / rows) - borderWidth;
|
const sqWidth = (Math.floor(Bangle.appRect.w - 48) / rows) - borderWidth;
|
||||||
const cellColors = [{bg:'#00FFFF', fg: '#000000'},
|
const cellColors = [{bg:'#00FFFF', fg: '#000000'},
|
||||||
|
|
@ -13,12 +27,8 @@ const cellChars = [
|
||||||
['0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
|
['0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
|
||||||
['0','I', 'II', 'III', 'IV', 'V', 'VI', 'VII','VIII', 'IX', 'X']
|
['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 = {
|
const scores = {
|
||||||
currentScore: 0,
|
currentScore: 0,
|
||||||
|
|
@ -78,12 +88,12 @@ const snapshot = {
|
||||||
updCounter: function() {
|
updCounter: function() {
|
||||||
this.counter = ++this.counter > this.interval ? 0 : this.counter;
|
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() {
|
write: function() {
|
||||||
require("Storage").writeJSON(this.snFileName, this.dump);
|
require("Storage").writeJSON(this.snFileName, this.dump);
|
||||||
},
|
},
|
||||||
read: function () {
|
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)) {
|
if ((typeof sn == "undefined") || (sn.gridsize !== rows * cols)) {
|
||||||
require("Storage").writeJSON(this.snFileName, this.dump);
|
require("Storage").writeJSON(this.snFileName, this.dump);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -101,7 +111,6 @@ const snapshot = {
|
||||||
});
|
});
|
||||||
this.dump.score = scores.currentScore;
|
this.dump.score = scores.currentScore;
|
||||||
this.dump.highScore = scores.highScore;
|
this.dump.highScore = scores.highScore;
|
||||||
this.dump.charIndex = charIndex;
|
|
||||||
},
|
},
|
||||||
make: function () {
|
make: function () {
|
||||||
this.updCounter();
|
this.updCounter();
|
||||||
|
|
@ -118,7 +127,7 @@ const snapshot = {
|
||||||
});
|
});
|
||||||
scores.currentScore = this.dump.score ? this.dump.score : 0;
|
scores.currentScore = this.dump.score ? this.dump.score : 0;
|
||||||
scores.highScore = this.dump.highScore ? this.dump.highScore : 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 () {
|
reset: function () {
|
||||||
|
|
@ -129,12 +138,11 @@ const snapshot = {
|
||||||
}
|
}
|
||||||
this.dump.score = 0;
|
this.dump.score = 0;
|
||||||
this.dump.highScore = scores.highScore;
|
this.dump.highScore = scores.highScore;
|
||||||
this.dump.charIndex = charIndex;
|
|
||||||
this.write();
|
this.write();
|
||||||
debug(() => console.log("reset D U M P E D!", this.dump));
|
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 = {
|
const buttons = {
|
||||||
all: [],
|
all: [],
|
||||||
draw: function () {
|
draw: function () {
|
||||||
|
|
@ -314,7 +322,7 @@ class Cell {
|
||||||
}
|
}
|
||||||
drawBg() {
|
drawBg() {
|
||||||
debug(()=>console.log("Drawbg!!"));
|
debug(()=>console.log("Drawbg!!"));
|
||||||
if (this.isRndm == true) {
|
if (this.isRndm) {
|
||||||
debug(()=>console.log('Random: (ax)', this.ax));
|
debug(()=>console.log('Random: (ax)', this.ax));
|
||||||
g.setColor(this.getColor(this.expVal).bg)
|
g.setColor(this.getColor(this.expVal).bg)
|
||||||
.fillRect(this.x0, this.y0, this.x1, this.y1)
|
.fillRect(this.x0, this.y0, this.x1, this.y1)
|
||||||
|
|
@ -365,7 +373,7 @@ class Cell {
|
||||||
this.isRndm = true;
|
this.isRndm = true;
|
||||||
}
|
}
|
||||||
drawRndmIndicator(){
|
drawRndmIndicator(){
|
||||||
if (this.isRndm == true) {
|
if (this.isRndm) {
|
||||||
debug(()=>console.log('Random: (ax)', this.ax));
|
debug(()=>console.log('Random: (ax)', this.ax));
|
||||||
g.setColor(this.getColor(0).bg)
|
g.setColor(this.getColor(0).bg)
|
||||||
.fillPoly(this.ax,this.ay,this.bx,this.by,this.cx,this.cy);
|
.fillPoly(this.ax,this.ay,this.bx,this.by,this.cx,this.cy);
|
||||||
|
|
@ -374,8 +382,9 @@ class Cell {
|
||||||
}
|
}
|
||||||
|
|
||||||
function undoGame() {
|
function undoGame() {
|
||||||
g.clear();
|
|
||||||
if (scores.lastScores.length > 0) {
|
if (scores.lastScores.length) {
|
||||||
|
g.clear();
|
||||||
allSquares.forEach(sq => {
|
allSquares.forEach(sq => {
|
||||||
sq.popFromUndo();
|
sq.popFromUndo();
|
||||||
sq.drawBg();
|
sq.drawBg();
|
||||||
|
|
@ -386,9 +395,9 @@ function undoGame() {
|
||||||
buttons.draw();
|
buttons.draw();
|
||||||
updUndoLvlIndex();
|
updUndoLvlIndex();
|
||||||
snapshot.make();
|
snapshot.make();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
}
|
}
|
||||||
Bangle.loadWidgets();
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
}
|
}
|
||||||
function addToUndo() {
|
function addToUndo() {
|
||||||
allSquares.forEach(sq => {
|
allSquares.forEach(sq => {
|
||||||
|
|
@ -487,8 +496,8 @@ function initGame() {
|
||||||
drawGrid();
|
drawGrid();
|
||||||
scores.draw();
|
scores.draw();
|
||||||
buttons.draw();
|
buttons.draw();
|
||||||
// Clock mode allows short-press on button to exit
|
// #settings Clock mode allows short-press on button to exit
|
||||||
Bangle.setUI("clock");
|
if(clockMode) Bangle.setUI("clock");
|
||||||
// Load widgets
|
// Load widgets
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
@ -507,8 +516,8 @@ function drawPopUp(message,cb) {
|
||||||
rDims.x+10, rDims.y2-40
|
rDims.x+10, rDims.y2-40
|
||||||
]);
|
]);
|
||||||
buttons.all.forEach(btn => {btn.disable();});
|
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 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-80, 54, btnAtribs.h, 'NO', 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();
|
btnYes.draw();
|
||||||
btnNo.draw();
|
btnNo.draw();
|
||||||
g.setColor('#000000');
|
g.setColor('#000000');
|
||||||
|
|
@ -560,14 +569,8 @@ function resetGame() {
|
||||||
* @param {function} func function to call like console.log()
|
* @param {function} func function to call like console.log()
|
||||||
*/
|
*/
|
||||||
const debug = (func) => {
|
const debug = (func) => {
|
||||||
switch (debugMode) {
|
if (debugMode) {
|
||||||
case "development":
|
if (typeof func === 'function') func();
|
||||||
if (typeof func === 'function') {
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "off":
|
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -690,13 +693,9 @@ function updUndoLvlIndex() {
|
||||||
.drawString(scores.lastScores.length, x, y);
|
.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('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));
|
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();
|
initGame();
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "game1024",
|
{ "id": "game1024",
|
||||||
"name": "1024 Game",
|
"name": "1024 Game",
|
||||||
"shortName" : "1024 Game",
|
"shortName" : "1024 Game",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"icon": "game1024.png",
|
"icon": "game1024.png",
|
||||||
"screenshots": [ {"url":"screenshot.png" } ],
|
"screenshots": [ {"url":"screenshot.png" } ],
|
||||||
"readme":"README.md",
|
"readme":"README.md",
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
"supports" : ["BANGLEJS2"],
|
"supports" : ["BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"game1024.app.js","url":"app.js"},
|
{"name":"game1024.app.js","url":"app.js"},
|
||||||
|
{"name":"game1024.settings.js","url":"settings.js"},
|
||||||
{"name":"game1024.img","url":"app-icon.js","evaluate":true}
|
{"name":"game1024.img","url":"app-icon.js","evaluate":true}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 3.4 KiB |
|
|
@ -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);
|
||||||
|
})
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"screenshots": [{"url":"bangle1-high-contrast-clock-screenshot.png"}],
|
"screenshots": [{"url":"bangle1-high-contrast-clock-screenshot.png"}],
|
||||||
"supports": ["BANGLEJS"],
|
"supports": ["BANGLEJS"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"hcclock.app.js","url":"hcclock.app.js"},
|
{"name":"hcclock.app.js","url":"hcclock.app.js"},
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}],
|
"screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}],
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"intclock.app.js","url":"app.js"},
|
{"name":"intclock.app.js","url":"app.js"},
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"icon": "intervals.png",
|
"icon": "intervals.png",
|
||||||
"tags": "",
|
"tags": "",
|
||||||
"supports": ["BANGLEJS"],
|
"supports": ["BANGLEJS"],
|
||||||
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"intervals.app.js","url":"intervals.app.js"},
|
{"name":"intervals.app.js","url":"intervals.app.js"},
|
||||||
{"name":"intervals.img","url":"intervals-icon.js","evaluate":true}
|
{"name":"intervals.img","url":"intervals-icon.js","evaluate":true}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"tags": "tool,system,ios,apple,messages,notifications",
|
"tags": "tool,system,ios,apple,messages,notifications",
|
||||||
"dependencies": {"messages":"app"},
|
"dependencies": {"messages":"app"},
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"ios.app.js","url":"app.js"},
|
{"name":"ios.app.js","url":"app.js"},
|
||||||
{"name":"ios.img","url":"app-icon.js","evaluate":true},
|
{"name":"ios.img","url":"app-icon.js","evaluate":true},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
|
@ -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.
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -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);
|
||||||
|
}});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -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"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.7 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
|
@ -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)
|
||||||
|
After Width: | Height: | Size: 506 B |
|
|
@ -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);
|
||||||
|
}});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -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"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -17,3 +17,4 @@
|
||||||
0.17: Settings for mph/kph and other minor improvements.
|
0.17: Settings for mph/kph and other minor improvements.
|
||||||
0.18: Fullscreen mode can now be enabled or disabled in the settings.
|
0.18: Fullscreen mode can now be enabled or disabled in the settings.
|
||||||
0.19: Alarms can not go bigger than 100.
|
0.19: Alarms can not go bigger than 100.
|
||||||
|
0.20: Use alarm for alarm functionality instead of own implementation.
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
A simple LCARS inspired clock.
|
A simple LCARS inspired clock.
|
||||||
Note: To display the steps, the wpedom app is required. To show weather data
|
Note: To display the steps, the wpedom app is required. To show weather data
|
||||||
such as temperature, humidity or window you BangleJS must be connected
|
such as temperature, humidity or window you BangleJS must be connected
|
||||||
with Gadgetbride and the weather app must be installed.
|
with Gadgetbride and the weather app must be installed. To use the timer
|
||||||
|
the "sched" app must be installed on your device.
|
||||||
|
|
||||||
## Control
|
## Control
|
||||||
* Tap left / right to change between screens.
|
* Tap left / right to change between screens.
|
||||||
|
|
@ -15,7 +16,7 @@ with Gadgetbride and the weather app must be installed.
|
||||||
* Tab on left/right to switch between different screens.
|
* Tab on left/right to switch between different screens.
|
||||||
* Cusomizable data that is shown on screen 1 (steps, weather etc.)
|
* Cusomizable data that is shown on screen 1 (steps, weather etc.)
|
||||||
* Shows random and real images of planets.
|
* Shows random and real images of planets.
|
||||||
* Tap on top/bottom of screen 1 to activate an alarm.
|
* Tap on top/bottom of screen 1 to activate an alarm. Depends on widtmr.
|
||||||
* The lower orange line indicates the battery level.
|
* The lower orange line indicates the battery level.
|
||||||
* Display graphs (day or month) for steps + hrm on the second screen.
|
* Display graphs (day or month) for steps + hrm on the second screen.
|
||||||
|
|
||||||
|
|
@ -36,8 +37,9 @@ Access different screens via tap on the left/ right side of the screen
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
## Creator
|
||||||
|
- [David Peer](https://github.com/peerdavid)
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
- [David Peer](https://github.com/peerdavid).
|
- [Adam Schmalhofer](https://github.com/adamschmalhofer)
|
||||||
- [Adam Schmalhofer](https://github.com/adamschmalhofer).
|
- [Jon Warrington](https://github.com/BartokW)
|
||||||
- [Jon Warrington](https://github.com/BartokW).
|
|
||||||
|
|
|
||||||