Merge branch 'master' of https://github.com/peerdavid/BangleApps
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App
|
||||
0.02: app keeps track of statistics now
|
||||
0.03: Fix bug in valid word detection
|
||||
|
|
|
|||
|
|
@ -110,7 +110,12 @@ class Wordle {
|
|||
}
|
||||
}
|
||||
addGuess(w) {
|
||||
if ((this.words.indexOf(w.toLowerCase())%5)!=0) {
|
||||
let idx = -1;
|
||||
do{
|
||||
idx = this.words.indexOf(w.toLowerCase(), idx+1);
|
||||
}
|
||||
while(idx !== -1 && idx%5 !== 0);
|
||||
if(idx%5 !== 0) {
|
||||
E.showAlert(w+"\nis not a word", "Invalid word").then(function() {
|
||||
layout = getKeyLayout("");
|
||||
wordle.render(true);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Bordle",
|
||||
"shortName":"Bordle",
|
||||
"icon": "app.png",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "Bangle version of a popular word search game",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
# Banglejs - Touchscreen calibration
|
||||
A simple calibration app for the touchscreen
|
||||
|
||||
## Usage
|
||||
|
||||
Once lauched touch the cross that appear on the screen to make
|
||||
another spawn elsewhere.
|
||||
|
||||
each new touch on the screen will help to calibrate the offset
|
||||
of your finger on the screen. After five or more input, press
|
||||
the button to save the calibration and close the application.
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkB/4AJ+EPBhQXg+BBDCyJaGGR5zIDBoQEL4QYOLYR3GBIouJR5AYBGBILBU5QMGFwgiFX4wwIEI4XGGBAgHd44+HD44XHNw4XWM5IIHCIoXWV5IXICQgXvLxAAKCYYXh5nMC6n8C4PPC5MAAA8PC4ZxBACAXOI653hU5zvJABASEC5PwHI4XcMBIXICIoXXJBAXHCAwXXJBAXHB5AfGC4ygJEAwXGQ5BoIQxoiDBYgXECwIuIBgb5ECIQJFGBQmCC4QHEDBwAFCxoYICx5ZELZoZJFiIXpA="))
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
class BanglejsApp {
|
||||
constructor() {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.settings = {
|
||||
xoffset: 0,
|
||||
yoffset: 0,
|
||||
};
|
||||
}
|
||||
|
||||
load_settings() {
|
||||
let settings = require('Storage').readJSON('calibration.json', true) || {active: false};
|
||||
|
||||
// do nothing if the calibration is deactivated
|
||||
if (settings.active === true) {
|
||||
// cancel the calibration offset
|
||||
Bangle.on('touch', function(button, xy) {
|
||||
xy.x += settings.xoffset;
|
||||
xy.y += settings.yoffset;
|
||||
});
|
||||
}
|
||||
if (!settings.xoffset) settings.xoffset = 0;
|
||||
if (!settings.yoffset) settings.yoffset = 0;
|
||||
|
||||
console.log('loaded settings:');
|
||||
console.log(settings);
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
save_settings() {
|
||||
this.settings.active = true;
|
||||
this.settings.reload = false;
|
||||
require('Storage').writeJSON('calibration.json', this.settings);
|
||||
|
||||
console.log('saved settings:');
|
||||
console.log(this.settings);
|
||||
}
|
||||
|
||||
explain() {
|
||||
/*
|
||||
* TODO:
|
||||
* Present how to use the application
|
||||
*
|
||||
*/
|
||||
}
|
||||
|
||||
drawTarget() {
|
||||
this.x = 16 + Math.floor(Math.random() * (g.getWidth() - 32));
|
||||
this.y = 40 + Math.floor(Math.random() * (g.getHeight() - 80));
|
||||
|
||||
g.clearRect(0, 24, g.getWidth(), g.getHeight() - 24);
|
||||
g.drawLine(this.x, this.y - 5, this.x, this.y + 5);
|
||||
g.drawLine(this.x - 5, this.y, this.x + 5, this.y);
|
||||
g.setFont('Vector', 10);
|
||||
g.drawString('current offset: ' + this.settings.xoffset + ', ' + this.settings.yoffset, 0, 24);
|
||||
}
|
||||
|
||||
setOffset(xy) {
|
||||
this.settings.xoffset = Math.round((this.settings.xoffset + (this.x - Math.floor((this.x + xy.x)/2)))/2);
|
||||
this.settings.yoffset = Math.round((this.settings.yoffset + (this.y - Math.floor((this.y + xy.y)/2)))/2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
E.srand(Date.now());
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
calibration = new BanglejsApp();
|
||||
calibration.load_settings();
|
||||
|
||||
let modes = {
|
||||
mode : 'custom',
|
||||
btn : function(n) {
|
||||
calibration.save_settings(this.settings);
|
||||
load();
|
||||
},
|
||||
touch : function(btn, xy) {
|
||||
calibration.setOffset(xy);
|
||||
calibration.drawTarget();
|
||||
},
|
||||
};
|
||||
Bangle.setUI(modes);
|
||||
calibration.drawTarget();
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
let cal_settings = require('Storage').readJSON("calibration.json", true) || {active: false};
|
||||
Bangle.on('touch', function(button, xy) {
|
||||
// do nothing if the calibration is deactivated
|
||||
if (cal_settings.active === false) return;
|
||||
|
||||
// reload the calibration offset at each touch event /!\ bad for the flash memory
|
||||
if (cal_settings.reload === true) {
|
||||
cal_settings = require('Storage').readJSON("calibration.json", true);
|
||||
}
|
||||
|
||||
// apply the calibration offset
|
||||
xy.x += cal_settings.xoffset;
|
||||
xy.y += cal_settings.yoffset;
|
||||
});
|
||||
|
After Width: | Height: | Size: 451 B |
|
|
@ -0,0 +1,17 @@
|
|||
{ "id": "calibration",
|
||||
"name": "Touchscreen Calibration",
|
||||
"shortName":"Calibration",
|
||||
"icon": "calibration.png",
|
||||
"version":"1.00",
|
||||
"description": "A simple calibration app for the touchscreen",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"tags": "tool",
|
||||
"storage": [
|
||||
{"name":"calibration.app.js","url":"app.js"},
|
||||
{"name":"calibration.boot.js","url":"boot.js"},
|
||||
{"name":"calibration.settings.js","url":"settings.js"},
|
||||
{"name":"calibration.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"calibration.json"}]
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
(function(back) {
|
||||
var FILE = "calibration.json";
|
||||
var settings = Object.assign({
|
||||
active: true,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
"" : { "title" : "Calibration" },
|
||||
"< Back" : () => back(),
|
||||
'Active': {
|
||||
value: !!settings.active,
|
||||
format: v => v? "On":"Off",
|
||||
onchange: v => {
|
||||
settings.active = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
|
|
@ -6,4 +6,5 @@
|
|||
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.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
|
||||
0.09: Added settings menu, removed symbol selection button (*), added highscore reset
|
||||
0.10: fixed clockmode in settings
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "game1024",
|
||||
"name": "1024 Game",
|
||||
"shortName" : "1024 Game",
|
||||
"version": "0.09",
|
||||
"version": "0.10",
|
||||
"icon": "game1024.png",
|
||||
"screenshots": [ {"url":"screenshot.png" } ],
|
||||
"readme":"README.md",
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@
|
|||
}
|
||||
},
|
||||
"Exit press:": {
|
||||
value: !settings.debugMode, // ! converts undefined to true
|
||||
value: !settings.clockMode, // ! converts undefined to true
|
||||
format: v => v?"short":"long",
|
||||
onchange: v => {
|
||||
settings.debugMode = v;
|
||||
settings.clockMode = v;
|
||||
writeSettings();
|
||||
},
|
||||
},
|
||||
|
|
@ -67,4 +67,4 @@
|
|||
}
|
||||
// Show the menu
|
||||
E.showMenu(settingsMenu);
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.01: New Keyboard!
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Morse Keyboard
|
||||
|
||||
A library that provides the ability to input text by entering morse code.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
* Press `BTN1` to input a dot, `BTN3` to input a dash, and `BTN2` to accept the
|
||||
character for your current input.
|
||||
* Long-press `BTN1` to toggle UPPERCASE for your next character.
|
||||
* Long-press `BTN2` to finish editing.
|
||||
* Tap the left side of the screen for backspace.
|
||||
* Swipe left/right to move the cursor.
|
||||
* Input three spaces in a row for a newline.
|
||||
|
||||
The top/bottom of the screen show which characters start with your current input,
|
||||
so basically you just look which side includes the letter you want to type, and
|
||||
press that button to narrow your selection, until it appears next to `BTN2`.
|
||||
|
||||
|
||||
## For Developers
|
||||
|
||||
See the README for `kbswipe`/`kbtouch` for instructions on how to use this in your app.
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 693 KiB |
|
|
@ -0,0 +1,247 @@
|
|||
exports.input = function(options) {
|
||||
options = options || {};
|
||||
let text = options.text;
|
||||
if ("string"!= typeof text) text = "";
|
||||
let code = "",
|
||||
cur = text.length, // cursor position
|
||||
uc = !text.length, // uppercase
|
||||
spc = 0; // consecutive spaces entered
|
||||
|
||||
const codes = {
|
||||
// letters
|
||||
"a": ".-",
|
||||
"b": "-...",
|
||||
"c": "-.-.",
|
||||
"d": "-..",
|
||||
"e": ".",
|
||||
// no é
|
||||
"f": "..-.",
|
||||
"g": "--.",
|
||||
"h": "....",
|
||||
"i": "..",
|
||||
"j": ".---",
|
||||
"k": "-.-",
|
||||
"l": ".-..",
|
||||
"m": "--",
|
||||
"n": "-.",
|
||||
"o": "---",
|
||||
"p": ".--.",
|
||||
"q": "--.-",
|
||||
"r": ".-.",
|
||||
"s": "...",
|
||||
"t": "-",
|
||||
"u": "..-",
|
||||
"v": "...-",
|
||||
"w": ".--",
|
||||
"x": "-..-",
|
||||
"y": "-.--",
|
||||
"z": "--..",
|
||||
//digits
|
||||
"1": ".----",
|
||||
"2": "..---",
|
||||
"3": "...--",
|
||||
"4": "....-",
|
||||
"5": ".....",
|
||||
"6": "-....",
|
||||
"7": "--...",
|
||||
"8": "---..",
|
||||
"9": "----.",
|
||||
"0": "-----",
|
||||
// punctuation
|
||||
".": ".-.-.-",
|
||||
",": "--..--",
|
||||
":": "---...",
|
||||
"?": "..--..",
|
||||
"!": "-.-.--",
|
||||
"'": ".----.",
|
||||
"-": "-....-",
|
||||
"_": "..--.-",
|
||||
"/": "-..-.",
|
||||
"(": "-.--.",
|
||||
")": "-.--.-",
|
||||
"\"": ".-..-.",
|
||||
"=": "-...-",
|
||||
"+": ".-.-.",
|
||||
"*": "-..-",
|
||||
"@": ".--.-.",
|
||||
"$": "...-..-",
|
||||
"&": ".-...",
|
||||
}, chars = Object.keys(codes);
|
||||
|
||||
function choices(start) {
|
||||
return chars.filter(char => codes[char].startsWith(start));
|
||||
}
|
||||
function char(code) {
|
||||
if (code==="") return " ";
|
||||
for(const char in codes) {
|
||||
if (codes[char]===code) return char;
|
||||
}
|
||||
const c = choices(code);
|
||||
if (c.length===1) return c[0]; // "-.-.-" is nothing, and only "-.-.--"(!) starts with it
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
function update() {
|
||||
let dots = [], dashes = [];
|
||||
layout.pick.label = (code==="" ? " " : "");
|
||||
choices(code).forEach(char => {
|
||||
const c = codes[char];
|
||||
if (c===code) {
|
||||
layout.pick.label = char;
|
||||
}
|
||||
const next = c.substring(code.length, code.length+1);
|
||||
if (next===".") dots.push(char);
|
||||
else if (next==="-") dashes.push(char);
|
||||
});
|
||||
if (!code && spc>1) layout.pick.label = atob("ABIYAQAAAAAAAAAABwABwABwABwABwABwOBwOBwOBxwBxwBxwB/////////xwABwABwAAOAAOAAOAA==");
|
||||
g.setFont("6x8:2");
|
||||
const wrap = t => g.wrapString(t, Bangle.appRect.w-60).join("\n");
|
||||
layout.del.label = cur ? atob("AAwIAQ/hAiKkEiKhAg/gAA==") : " ";
|
||||
layout.code.label = code;
|
||||
layout.dots.label = wrap(dots.join(" "));
|
||||
layout.dashes.label = wrap(dashes.join(" "));
|
||||
if (uc) {
|
||||
layout.pick.label = layout.pick.label.toUpperCase();
|
||||
layout.dots.label = layout.dots.label.toUpperCase();
|
||||
layout.dashes.label = layout.dashes.label.toUpperCase();
|
||||
}
|
||||
let label = text.slice(0, cur)+"|"+text.slice(cur);
|
||||
layout.text.label = g.wrapString(label, Bangle.appRect.w-80).join("\n")
|
||||
.replace("|", atob("AAwQAfPPPAwAwAwAwAwAwAwAwAwAwAwAwPPPPA=="));
|
||||
layout.update();
|
||||
layout.render();
|
||||
}
|
||||
|
||||
function add(d) {
|
||||
code += d;
|
||||
const l = choices(code).length;
|
||||
if (l===1) done();
|
||||
else if (l<1) {
|
||||
Bangle.buzz(20);
|
||||
code = code.slice(0, -1);
|
||||
} else update();
|
||||
}
|
||||
function del() {
|
||||
if (code.length) code = code.slice(0, -1); // delete last dot/dash
|
||||
else if (cur) { // delete char at cursor
|
||||
text = text.slice(0, cur-1)+text.slice(cur);
|
||||
cur--;
|
||||
} else Bangle.buzz(20); // (already) at start of text
|
||||
spc = 0;
|
||||
uc = false;
|
||||
update();
|
||||
}
|
||||
|
||||
function done() {
|
||||
let c = char(code);
|
||||
if (c!==null) {
|
||||
if (uc) c = c.toUpperCase();
|
||||
uc = false;
|
||||
text = text.slice(0, cur)+c+text.slice(cur);
|
||||
cur++;
|
||||
code = "";
|
||||
if (c===" ") spc++;
|
||||
else spc = 0;
|
||||
if (spc>=3) {
|
||||
text = text.slice(0, cur-3)+"\n"+text.slice(cur);
|
||||
cur -= 2;
|
||||
uc = true;
|
||||
spc = 0;
|
||||
}
|
||||
update();
|
||||
} else {
|
||||
console.log(`No char for ${code}!`);
|
||||
Bangle.buzz(20);
|
||||
}
|
||||
}
|
||||
|
||||
const Layout = require("Layout");
|
||||
let layout = new Layout({
|
||||
type: "h", c: [
|
||||
{
|
||||
type: "v", width: Bangle.appRect.w-8, bgCol: g.theme.bg, c: [
|
||||
{id: "dots", type: "txt", font: "6x8:2", label: "", fillx: 1, bgCol: g.theme.bg},
|
||||
{filly: 1, bgCol: g.theme.bg},
|
||||
{
|
||||
type: "h", fillx: 1, c: [
|
||||
{id: "del", type: "txt", font: "6x8", label: "<X"},
|
||||
{width: 5, bgCol: g.theme.bg},
|
||||
{id: "text", type: "txt", font: "6x8:2", col: g.theme.fg2, bgCol: g.theme.bg2},
|
||||
{fillx: 1, bgCol: g.theme.bg},
|
||||
{id: "code", type: "txt", font: "6x8", label: "", bgCol: g.theme.bg},
|
||||
{width: 5, bgCol: g.theme.bg},
|
||||
{id: "pick", type: "txt", font: "6x8:3", label: "", col: g.theme.fgH, bgCol: g.theme.bgH},
|
||||
],
|
||||
},
|
||||
{filly: 1, bgCol: g.theme.bg},
|
||||
{id: "dashes", type: "txt", font: "6x8:2", label: "", fillx: 1, bgCol: g.theme.bg},
|
||||
]
|
||||
},
|
||||
// button labels (rotated 90 degrees)
|
||||
{
|
||||
type: "v", pad: 1, filly: 1, c: ["<.", "^", "|"].map(l =>
|
||||
({type: "txt", font: "6x8", height: Math.floor(Bangle.appRect.h/3), r: 1, label: l})
|
||||
)
|
||||
}
|
||||
]
|
||||
});
|
||||
g.reset().clear();
|
||||
update();
|
||||
|
||||
if (Bangle.btnWatches) Bangle.btnWatches.forEach(clearWatch);
|
||||
Bangle.btnWatches = [];
|
||||
|
||||
// BTN1: press for dot, long-press to toggle uppercase
|
||||
let ucTimeout;
|
||||
const UC_TIME = 500;
|
||||
Bangle.btnWatches.push(setWatch(e => {
|
||||
if (ucTimeout) clearTimeout(ucTimeout);
|
||||
ucTimeout = null;
|
||||
if (e.state) {
|
||||
// pressed: start UpperCase toggle timer
|
||||
ucTimeout = setTimeout(() => {
|
||||
ucTimeout = null;
|
||||
uc = !uc;
|
||||
update();
|
||||
}, UC_TIME);
|
||||
} else if (e.time-e.lastTime<UC_TIME/1000) add(".");
|
||||
}, BTN1, {repeat: true, edge: "both"}));
|
||||
|
||||
// BTN2: press to pick current character, long press to enter text
|
||||
let enterTimeout;
|
||||
const ENTER_TIME = 1000;
|
||||
Bangle.btnWatches.push(setWatch(e => {
|
||||
if (enterTimeout) clearTimeout(enterTimeout);
|
||||
enterTimeout = null;
|
||||
if (e.state) {
|
||||
// pressed: start UpperCase toggle timer
|
||||
enterTimeout = setTimeout(() => {
|
||||
enterTimeout = null;
|
||||
resolve(text);
|
||||
}, ENTER_TIME);
|
||||
} else if (e.time-e.lastTime<ENTER_TIME/1000) done();
|
||||
}, BTN2, {repeat: true, edge: "both"}));
|
||||
|
||||
// BTN3: press for dash (long-press is hardcoded to device reboot)
|
||||
Bangle.btnWatches.push(setWatch(() => {
|
||||
add("-");
|
||||
}, BTN3, {repeat: true, edge: "falling"}));
|
||||
|
||||
// Left-hand side: backspace
|
||||
if (Bangle.touchHandler) Bangle.removeListener("touch", Bangle.touchHandler);
|
||||
Bangle.touchHandler = side => {
|
||||
if (side===1) del();
|
||||
};
|
||||
Bangle.on("touch", Bangle.touchHandler);
|
||||
|
||||
// swipe: move cursor
|
||||
if (Bangle.swipeHandler) Bangle.removeListener("swipe", Bangle.swipeHandler);
|
||||
Bangle.swipeHandler = dir => {
|
||||
cur = Math.max(0, Math.min(text.length, cur+dir));
|
||||
update();
|
||||
};
|
||||
Bangle.on("swipe", Bangle.swipeHandler);
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "kbmorse",
|
||||
"name": "Morse keyboard",
|
||||
"version": "0.01",
|
||||
"description": "A library for text input as morse code",
|
||||
"icon": "app.png",
|
||||
"type": "textinput",
|
||||
"tags": "keyboard",
|
||||
"supports" : ["BANGLEJS"],
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"textinput","url":"lib.js"}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
|
@ -0,0 +1 @@
|
|||
0.01: New keyboard
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Multitap Keyboard
|
||||
|
||||
A library that provides the ability to input text in a style familiar to anyone who had a mobile phone before they went all touchscreen.
|
||||
|
||||
Swipe right for Space, left for Backspace, and up/down for Caps lock. Tap the '?' button in the app if you need a reminder!
|
||||
|
||||
At time of writing, only the [Noteify app](http://microco.sm/out/Ffe9i) uses a keyboard.
|
||||
|
||||
Uses the multitap keypad logic originally from here: http://www.espruino.com/Morse+Code+Texting
|
||||
|
||||

|
||||

|
||||
|
||||
Written by: [Sir Indy](https://github.com/sir-indy) and [Thyttan](https://github.com/thyttan)
|
||||
|
||||
For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
After Width: | Height: | Size: 685 B |
|
|
@ -0,0 +1,145 @@
|
|||
//Multitap logic originally from here: http://www.espruino.com/Morse+Code+Texting
|
||||
|
||||
exports.input = function(options) {
|
||||
options = options||{};
|
||||
var text = options.text;
|
||||
if ("string"!=typeof text) text="";
|
||||
|
||||
var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {};
|
||||
if (settings.firstLaunch===undefined) { settings.firstLaunch = true; }
|
||||
if (settings.charTimeout===undefined) { settings.charTimeout = 500; }
|
||||
|
||||
var fontSize = "6x15";
|
||||
var Layout = require("Layout");
|
||||
var letters = {
|
||||
"1":".,!?1","2":"ABC2","3":"DEF3",
|
||||
"4":"GHI4","5":"JKL5","6":"MNO6",
|
||||
"7":"PQRS7","8":"TUV80","9":"WXYZ9",
|
||||
};
|
||||
var helpMessage = 'swipe:\nRight: Space\nLeft:Backspace\nUp/Down: Caps lock\n';
|
||||
|
||||
var charTimeout; // timeout after a key is pressed
|
||||
var charCurrent; // current character (index in letters)
|
||||
var charIndex; // index in letters[charCurrent]
|
||||
var caps = true;
|
||||
var layout;
|
||||
|
||||
function displayText() {
|
||||
layout.clear(layout.text);
|
||||
layout.text.label = text.slice(-12);
|
||||
layout.render(layout.text);
|
||||
}
|
||||
|
||||
function backspace() {
|
||||
// remove the timeout if we had one
|
||||
if (charTimeout!==undefined) {
|
||||
clearTimeout(charTimeout);
|
||||
charTimeout = undefined;
|
||||
}
|
||||
text = text.slice(0, -1);
|
||||
newCharacter();
|
||||
}
|
||||
|
||||
function setCaps() {
|
||||
caps = !caps;
|
||||
for (var key in letters) {
|
||||
layout[key].label = caps ? letters[key].toUpperCase() : letters[key].toLowerCase();
|
||||
}
|
||||
layout.render();
|
||||
}
|
||||
|
||||
function newCharacter(ch) {
|
||||
displayText();
|
||||
charCurrent = ch;
|
||||
charIndex = 0;
|
||||
}
|
||||
|
||||
function onKeyPad(key) {
|
||||
// remove the timeout if we had one
|
||||
if (charTimeout!==undefined) {
|
||||
clearTimeout(charTimeout);
|
||||
charTimeout = undefined;
|
||||
}
|
||||
// work out which char was pressed
|
||||
if (key==charCurrent) {
|
||||
charIndex = (charIndex+1) % letters[charCurrent].length;
|
||||
text = text.slice(0, -1);
|
||||
} else {
|
||||
newCharacter(key);
|
||||
}
|
||||
var newLetter = letters[charCurrent][charIndex];
|
||||
text += (caps ? newLetter.toUpperCase() : newLetter.toLowerCase());
|
||||
displayText();
|
||||
// set a timeout
|
||||
charTimeout = setTimeout(function() {
|
||||
charTimeout = undefined;
|
||||
newCharacter();
|
||||
}, settings.charTimeout);
|
||||
}
|
||||
|
||||
function onSwipe(dirLeftRight, dirUpDown) {
|
||||
if (dirUpDown) {
|
||||
setCaps();
|
||||
} else if (dirLeftRight == 1) {
|
||||
text += ' ';
|
||||
newCharacter();
|
||||
} else if (dirLeftRight == -1) {
|
||||
backspace();
|
||||
}
|
||||
}
|
||||
|
||||
function onHelp(resolve,reject) {
|
||||
Bangle.removeListener("swipe", onSwipe);
|
||||
E.showPrompt(
|
||||
helpMessage, {title: "Help", buttons : {"Ok":true}}
|
||||
).then(function(v) {
|
||||
Bangle.on('swipe', onSwipe);
|
||||
generateLayout(resolve,reject);
|
||||
layout.render();
|
||||
});
|
||||
}
|
||||
|
||||
function generateLayout(resolve,reject) {
|
||||
layout = new Layout( {
|
||||
type:"v", c: [
|
||||
{type:"h", c: [
|
||||
{type:"txt", font:"12x20", label:text.slice(-12), id:"text", fillx:1},
|
||||
{type:"btn", font:'6x8', label:'?', cb: l=>onHelp(resolve,reject), filly:1 },
|
||||
]},
|
||||
{type:"h", c: [
|
||||
{type:"btn", font:fontSize, label:letters[1], cb: l=>onKeyPad(1), id:'1', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[2], cb: l=>onKeyPad(2), id:'2', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[3], cb: l=>onKeyPad(3), id:'3', fillx:1, filly:1 },
|
||||
]},
|
||||
{type:"h", filly:1, c: [
|
||||
{type:"btn", font:fontSize, label:letters[4], cb: l=>onKeyPad(4), id:'4', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[5], cb: l=>onKeyPad(5), id:'5', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[6], cb: l=>onKeyPad(6), id:'6', fillx:1, filly:1 },
|
||||
]},
|
||||
{type:"h", filly:1, c: [
|
||||
{type:"btn", font:fontSize, label:letters[7], cb: l=>onKeyPad(7), id:'7', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[8], cb: l=>onKeyPad(8), id:'8', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[9], cb: l=>onKeyPad(9), id:'9', fillx:1, filly:1 },
|
||||
]},
|
||||
]
|
||||
},{back: ()=>{
|
||||
Bangle.setUI();
|
||||
Bangle.removeListener("swipe", onSwipe);
|
||||
g.clearRect(Bangle.appRect);
|
||||
resolve(text);
|
||||
}});
|
||||
}
|
||||
|
||||
return new Promise((resolve,reject) => {
|
||||
g.clearRect(Bangle.appRect);
|
||||
if (settings.firstLaunch) {
|
||||
onHelp(resolve,reject);
|
||||
settings.firstLaunch = false;
|
||||
require('Storage').writeJSON("kbmulti.settings.json", settings);
|
||||
} else {
|
||||
generateLayout(resolve,reject);
|
||||
Bangle.on('swipe', onSwipe);
|
||||
layout.render();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{ "id": "kbmulti",
|
||||
"name": "Multitap keyboard",
|
||||
"version":"0.01",
|
||||
"description": "A library for text input via multitap/T9 style keypad",
|
||||
"icon": "app.png",
|
||||
"type":"textinput",
|
||||
"tags": "keyboard",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot_1.png"},{"url":"screenshot_2.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"textinput","url":"lib.js"},
|
||||
{"name":"kbmulti.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"kbmulti.settings.json"}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -0,0 +1,31 @@
|
|||
(function(back) {
|
||||
function settings() {
|
||||
var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {};
|
||||
if (settings.firstLaunch===undefined) { settings.firstLaunch = true; }
|
||||
if (settings.charTimeout===undefined) { settings.charTimeout = 500; }
|
||||
return settings;
|
||||
}
|
||||
|
||||
function updateSetting(setting, value) {
|
||||
var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {};
|
||||
settings[setting] = value;
|
||||
require('Storage').writeJSON("kbmulti.settings.json", settings);
|
||||
}
|
||||
|
||||
var mainmenu = {
|
||||
"" : { "title" : /*LANG*/"Multitap keyboard" },
|
||||
"< Back" : back,
|
||||
/*LANG*/'Character selection timeout [ms]': {
|
||||
value: settings().charTimeout,
|
||||
min: 200, max: 1500, step : 50,
|
||||
format: v => v,
|
||||
onchange: v => updateSetting("charTimeout", v),
|
||||
},
|
||||
/*LANG*/'Show help on first launch': {
|
||||
value: !!settings().firstLaunch,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => updateSetting("firstLaunch", v)
|
||||
}
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
})
|
||||
|
|
@ -564,7 +564,7 @@ var locales = {
|
|||
month: "Janeiro,Fevereiro,Março,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro",
|
||||
abday: "Dom,Seg,Ter,Qua,Qui,Sex,Sab",
|
||||
day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado",
|
||||
trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "certo", on: "ligado", off: "desligado" }
|
||||
trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "confirmar", on: "ativado", off: "desativado" }
|
||||
},
|
||||
"cs_CZ": { // THIS NEVER WORKED PROPERLY - many chars are not in the ISO8859-1 codepage and we use CODEPAGE_CONVERSIONS
|
||||
lang: "cs_CZ",
|
||||
|
|
|
|||
|
|
@ -49,3 +49,5 @@
|
|||
0.34: Don't buzz for 'map' update messages
|
||||
0.35: Reset graphics colors before rendering a message (possibly fix #1752)
|
||||
0.36: Ensure a new message plus an almost immediate deletion of that message doesn't load the messages app (fix #1362)
|
||||
0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@
|
|||
/* For example for maps:
|
||||
|
||||
// a message
|
||||
{"t":"add","id":1575479849,"src":"Hangouts","title":"A Name","body":"message contents"}
|
||||
require("messages").pushMessage({"t":"add","id":1575479849,"src":"Hangouts","title":"A Name","body":"message contents"})
|
||||
// maps
|
||||
{"t":"add","id":1,"src":"Maps","title":"0 yd - High St","body":"Campton - 11:48 ETA","img":"GhqBAAAMAAAHgAAD8AAB/gAA/8AAf/gAP/8AH//gD/98B//Pg/4B8f8Afv+PP//n3/f5//j+f/wfn/4D5/8Aef+AD//AAf/gAD/wAAf4AAD8AAAeAAADAAA="}
|
||||
require("messages").pushMessage({"t":"add","id":1,"src":"Maps","title":"0 yd - High St","body":"Campton - 11:48 ETA","img":"GhqBAAAMAAAHgAAD8AAB/gAA/8AAf/gAP/8AH//gD/98B//Pg/4B8f8Afv+PP//n3/f5//j+f/wfn/4D5/8Aef+AD//AAf/gAD/wAAf4AAD8AAAeAAADAAA="});
|
||||
// call
|
||||
{"t":"add","id":"call","src":"Phone","name":"Bob","number":"12421312",positive:true,negative:true}
|
||||
require("messages").pushMessage({"t":"add","id":"call","src":"Phone","title":"Bob","body":"12421312",positive:true,negative:true})
|
||||
*/
|
||||
|
||||
var Layout = require("Layout");
|
||||
|
|
@ -67,9 +67,6 @@ function saveMessages() {
|
|||
require("Storage").writeJSON("messages.json",MESSAGES)
|
||||
}
|
||||
|
||||
function getBackImage() {
|
||||
return atob("FhYBAAAAEAAAwAAHAAA//wH//wf//g///BwB+DAB4EAHwAAPAAA8AADwAAPAAB4AAHgAB+AH/wA/+AD/wAH8AA==");
|
||||
}
|
||||
function getNotificationImage() {
|
||||
return atob("HBKBAD///8H///iP//8cf//j4//8f5//j/x/8//j/H//H4//4PB//EYj/44HH/Hw+P4//8fH//44///xH///g////A==");
|
||||
}
|
||||
|
|
@ -123,7 +120,6 @@ function getMessageImage(msg) {
|
|||
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
|
||||
if (s=="youtube") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
|
||||
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
|
||||
if (msg.id=="back") return getBackImage();
|
||||
return getNotificationImage();
|
||||
}
|
||||
function getMessageImageCol(msg,def) {
|
||||
|
|
@ -195,13 +191,13 @@ function showMapMessage(msg) {
|
|||
]});
|
||||
g.reset().clearRect(Bangle.appRect);
|
||||
layout.render();
|
||||
Bangle.setUI("updown",function() {
|
||||
// any input to mark as not new and return to menu
|
||||
function back() { // mark as not new and return to menu
|
||||
msg.new = false;
|
||||
saveMessages();
|
||||
layout = undefined;
|
||||
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:0});
|
||||
});
|
||||
}
|
||||
Bangle.setUI({mode:"updown", back: back}, back); // any input takes us back
|
||||
}
|
||||
|
||||
var updateLabelsInterval;
|
||||
|
|
@ -224,8 +220,6 @@ function showMusicMessage(msg) {
|
|||
var sliceLength = offset + maxLen > text.length ? text.length - offset : maxLen;
|
||||
return text.substr(offset, sliceLength).padEnd(maxLen, " ");
|
||||
}
|
||||
|
||||
|
||||
function back() {
|
||||
clearInterval(updateLabelsInterval);
|
||||
updateLabelsInterval = undefined;
|
||||
|
|
@ -254,7 +248,6 @@ function showMusicMessage(msg) {
|
|||
|
||||
layout = new Layout({ type:"v", c: [
|
||||
{type:"h", fillx:1, bgCol:g.theme.bg2, col: g.theme.fg2, c: [
|
||||
{ type:"btn", src:getBackImage, cb:back },
|
||||
{ type:"v", fillx:1, c: [
|
||||
{ type:"txt", font:fontMedium, bgCol:g.theme.bg2, label:artistName, pad:2, id:"artist" },
|
||||
{ type:"txt", font:fontMedium, bgCol:g.theme.bg2, label:albumName, pad:2, id:"album" }
|
||||
|
|
@ -267,7 +260,7 @@ function showMusicMessage(msg) {
|
|||
{type:"btn", pad:8, label:"\0"+atob("EhKBAMAB+AB/gB/wB/8B/+B//B//x//5//5//x//B/+B/8B/wB/gB+AB8ABw"), cb:()=>Bangle.musicControl("next")}, // next
|
||||
]}:{},
|
||||
{type:"txt", font:"6x8:2", label:msg.dur?fmtTime(msg.dur):"--:--" }
|
||||
]});
|
||||
]}, { back : back });
|
||||
g.reset().clearRect(Bangle.appRect);
|
||||
layout.render();
|
||||
|
||||
|
|
@ -302,12 +295,9 @@ function showMessageScroller(msg) {
|
|||
}, select : function(idx) {
|
||||
if (idx>=lines.length-2)
|
||||
showMessage(msg.id);
|
||||
}
|
||||
},
|
||||
back : () => showMessage(msg.id)
|
||||
});
|
||||
// ensure button-press on Bangle.js 2 takes us back
|
||||
if (process.env.HWVERSION>1) Bangle.btnWatches = [
|
||||
setWatch(() => showMessage(msg.id), BTN1, {repeat:1,edge:"falling"})
|
||||
];
|
||||
}
|
||||
|
||||
function showMessageSettings(msg) {
|
||||
|
|
@ -395,10 +385,8 @@ function showMessage(msgid) {
|
|||
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:0,openMusic:openMusic});
|
||||
}
|
||||
var buttons = [
|
||||
{type:"btn", src:getBackImage(), cb:goBack} // back
|
||||
];
|
||||
if (msg.positive) {
|
||||
buttons.push({fillx:1});
|
||||
buttons.push({type:"btn", src:getPosImage(), cb:()=>{
|
||||
msg.new = false; saveMessages();
|
||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||
|
|
@ -407,7 +395,7 @@ function showMessage(msgid) {
|
|||
}});
|
||||
}
|
||||
if (msg.negative) {
|
||||
buttons.push({fillx:1});
|
||||
if (buttons.length) buttons.push({width:32}); // nasty hack...
|
||||
buttons.push({type:"btn", src:getNegImage(), cb:()=>{
|
||||
msg.new = false; saveMessages();
|
||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||
|
|
@ -419,27 +407,23 @@ function showMessage(msgid) {
|
|||
|
||||
layout = new Layout({ type:"v", c: [
|
||||
{type:"h", fillx:1, bgCol:g.theme.bg2, col: g.theme.fg2, c: [
|
||||
{ type:"btn", src:getMessageImage(msg), col:getMessageImageCol(msg), pad: 3, cb:()=>{
|
||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||
showMessageSettings(msg);
|
||||
}},
|
||||
{ type:"v", fillx:1, c: [
|
||||
{type:"txt", font:fontSmall, label:msg.src||/*LANG*/"Message", bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2, halign:1 },
|
||||
title?{type:"txt", font:titleFont, label:title, bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2 }:{},
|
||||
]},
|
||||
{ type:"btn", src:getMessageImage(msg), col:getMessageImageCol(msg), pad: 3, cb:()=>{
|
||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||
showMessageSettings(msg);
|
||||
}},
|
||||
]},
|
||||
{type:"txt", font:bodyFont, label:body, fillx:1, filly:1, pad:2, cb:()=>{
|
||||
// allow tapping to show a larger version
|
||||
showMessageScroller(msg);
|
||||
} },
|
||||
{type:"h",fillx:1, c: buttons}
|
||||
]});
|
||||
]},{back:goBack});
|
||||
g.reset().clearRect(Bangle.appRect);
|
||||
layout.render();
|
||||
// ensure button-press on Bangle.js 2 takes us back
|
||||
if (process.env.HWVERSION>1) Bangle.btnWatches = [
|
||||
setWatch(goBack, BTN1, {repeat:1,edge:"falling"})
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -475,13 +459,12 @@ function checkMessages(options) {
|
|||
// Otherwise show a menu
|
||||
E.showScroller({
|
||||
h : 48,
|
||||
c : Math.max(MESSAGES.length+1,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
|
||||
c : Math.max(MESSAGES.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
|
||||
draw : function(idx, r) {"ram"
|
||||
var msg = MESSAGES[idx-1];
|
||||
var msg = MESSAGES[idx];
|
||||
if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH);
|
||||
else g.setColor(g.theme.fg);
|
||||
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
||||
if (idx==0) msg = {id:"back", title:"< Back"};
|
||||
if (!msg) return;
|
||||
var x = r.x+2, title = msg.title, body = msg.body;
|
||||
var img = getMessageImage(msg);
|
||||
|
|
@ -510,13 +493,12 @@ function checkMessages(options) {
|
|||
if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2);
|
||||
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
|
||||
},
|
||||
select : idx => {
|
||||
if (idx==0) load();
|
||||
else showMessage(MESSAGES[idx-1].id);
|
||||
}
|
||||
select : idx => showMessage(MESSAGES[idx].id),
|
||||
back : () => load()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function cancelReloadTimeout() {
|
||||
if (!unreadTimeout) return;
|
||||
clearTimeout(unreadTimeout);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.36",
|
||||
"version": "0.37",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge/Android",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
|||
|
|
@ -18,3 +18,6 @@ This app uses the [Scheduler library](https://banglejs.com/apps/?id=sched) and r
|
|||

|
||||
|
||||

|
||||
|
||||
## Web interface
|
||||
You can also add, edit or delete notes in the web interface, accessible with the download button.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="notes" class="container">
|
||||
<div class="columns">
|
||||
<div class="column col-9">
|
||||
<textarea class="form-input" id="note-new" placeholder="New note" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="column col-3">
|
||||
<button class="btn btn-default" id="btnAdd">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<script>
|
||||
var notesElement = document.getElementById("notes");
|
||||
var notes = {};
|
||||
|
||||
function getData() {
|
||||
// show loading window
|
||||
Util.showModal("Loading...");
|
||||
Util.readStorage(`noteify.json`,data=>{
|
||||
notes = JSON.parse(data || "[]");
|
||||
|
||||
// remove window
|
||||
Util.hideModal();
|
||||
|
||||
notes.forEach((note, i) => {
|
||||
const divColumn = document.createElement("div");
|
||||
divColumn.classList.add('columns');
|
||||
|
||||
const divColumn1 = document.createElement("div");
|
||||
divColumn1.classList.add('column');
|
||||
divColumn1.classList.add('col-9');
|
||||
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.id = "note" + i;
|
||||
textarea.classList.add('form-input');
|
||||
textarea.rows = 3;
|
||||
textarea.value = note.note;
|
||||
divColumn1.appendChild(textarea);
|
||||
divColumn.appendChild(divColumn1);
|
||||
|
||||
const divColumn2 = document.createElement("div");
|
||||
divColumn2.classList.add('column');
|
||||
divColumn2.classList.add('col-3');
|
||||
|
||||
const buttonSave = document.createElement("button");
|
||||
buttonSave.textContent = "Save";
|
||||
buttonSave.classList.add('btn');
|
||||
buttonSave.classList.add('btn-default');
|
||||
buttonSave.onclick = function() {
|
||||
notes[i].note = textarea.value;
|
||||
Util.writeStorage("noteify.json", JSON.stringify(notes));
|
||||
location.reload();
|
||||
}
|
||||
divColumn2.appendChild(buttonSave);
|
||||
|
||||
const buttonDelete = document.createElement("button");
|
||||
buttonDelete.classList.add('btn');
|
||||
buttonDelete.textContent = "Delete";
|
||||
buttonDelete.onclick = function() {
|
||||
notes[i].note = textarea.value;
|
||||
notes.splice(i, 1);
|
||||
Util.writeStorage("noteify.json", JSON.stringify(notes));
|
||||
location.reload(); // reload so we see current data
|
||||
}
|
||||
divColumn2.appendChild(buttonDelete);
|
||||
divColumn.appendChild(divColumn2);
|
||||
|
||||
notesElement.prepend(document.createElement("hr"));
|
||||
notesElement.prepend(divColumn);
|
||||
});
|
||||
|
||||
document.getElementById("btnAdd").addEventListener("click", function() {
|
||||
const note = document.getElementById("note-new").value;
|
||||
notes.push({"note": note});
|
||||
Util.writeStorage("noteify.json", JSON.stringify(notes));
|
||||
location.reload(); // reload so we see current data
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Called when app starts
|
||||
function onInit() {
|
||||
getData();
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
],
|
||||
"data": [{"name":"noteify.json"}],
|
||||
"dependencies": {"scheduler":"type","textinput":"type"},
|
||||
"interface": "interface.html",
|
||||
"screenshots": [
|
||||
{"url": "menu.png"},
|
||||
{"url": "note.png"},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function scrollX(){
|
|||
gfx.clearRect(0,gfx.getHeight()*(1/4),gfx.getWidth(),0);
|
||||
gfx.scroll(0,gfx.getHeight()/4);
|
||||
score++;
|
||||
if(typeof(m) != undefined && score>0){
|
||||
if(typeof m !== 'undefined' && score>0){
|
||||
clearInterval(m);
|
||||
m = setInterval(scrollY,Math.abs(100/score+15-0.1*score));}
|
||||
gfx.setColor(1,1,1);
|
||||
|
|
|
|||
|
|
@ -368,8 +368,8 @@ class TextBox {
|
|||
// x and y are the center points
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.text = (typeof text !== undefined) ? text : "Default";
|
||||
this.col = (typeof col !== undefined) ? col : red;
|
||||
this.text = text || "Default";
|
||||
this.col = col || red;
|
||||
|
||||
// console.log(`Constr TextBox ${this.text} -> Center: (${this.x}, ${this.y}) | Col ${this.col}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.0.1: Initial implementation
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
たばんち (tabanchi)
|
||||
===================
|
||||
|
||||
A Tamagotchi clone watch app for the BangleJS2 smartwatch.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
Written by pancake in 2022, powered by insomnia
|
||||
|
||||
Source repository: https://github.com/trufae/tabanchi
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* [x] 12/24 clock with HH:mm:ss
|
||||
* [x] Battery level indicator
|
||||
* [x] Eating meals and snacks
|
||||
* [x] Refusing to do things
|
||||
* [x] Getting sick
|
||||
* [x] Take a shower
|
||||
* [x] Switch on/off the light
|
||||
* [x] Status for happy/hunger/discipline
|
||||
* [ ] Evolutions
|
||||
* [ ] Hatching eggs
|
||||
* [x] Playing a game
|
||||
* [ ] Education
|
||||
* [x] Medicine
|
||||
* [ ] Death
|
||||
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* Original pixmaps taken from:
|
||||
- https://www.spriters-resource.com/resources/sheets/141/144400.png
|
||||
* Espruino Image converter:
|
||||
- https://www.espruino.com/Image+Converter
|
||||
* Tamagotchi Essentials
|
||||
- https://tamagotchi.fandom.com/wiki/Tamagotchi_(1996_Pet)
|
||||
* Tamagotchi Emulator Source (Java)
|
||||
- https://gist.github.com/aerospark/80c60e801398fd961e3f
|
||||
|
||||
Screenshots
|
||||
-----------
|
||||

|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
atob("MDDBAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gAAAAD//gAAAAD//gAAAAf//8AAAAf//8AAAAf//8AAAD/////gAD/////gAD/////gAf/////8Af/////8Af/////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8f//////gf//////gf//////gD/////gAD/////gAD/////gAAf///8AAAf///8AAAf///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
|
||||
|
After Width: | Height: | Size: 224 B |
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"id": "tabanchi",
|
||||
"name": "Tabanchi",
|
||||
"shortName": "Tabanchi",
|
||||
"version": "0.0.1",
|
||||
"type": "app",
|
||||
"description": "Tamagotchi WatchApp",
|
||||
"icon": "app.png",
|
||||
"allow_emulator": true,
|
||||
"tags": "watch, pet",
|
||||
"supports": [
|
||||
"BANGLEJS2"
|
||||
],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{
|
||||
"name": "tabanchi.app.js",
|
||||
"url": "app.js"
|
||||
},
|
||||
{
|
||||
"name": "tabanchi.img",
|
||||
"url": "app-icon.js",
|
||||
"evaluate": true
|
||||
}
|
||||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"url": "screenshot.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 554 KiB |
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Rename "Activity" in "Motion" and display the true values for it
|
||||
0.03: Add Banglejs 1 compatibility
|
||||
0.04: Fix settings bug
|
||||
0.05: Add altitude display (only Bangle.js 2)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ A clock displayed as a terminal cli.
|
|||
It can display :
|
||||
- time
|
||||
- date
|
||||
- altitude
|
||||
- hrm
|
||||
- motion
|
||||
- steps
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
var locale = require("locale");
|
||||
var fontColor = g.theme.dark ? "#0f0" : "#000";
|
||||
var heartRate = 0;
|
||||
var altitude = -9001;
|
||||
|
||||
// handling the differents versions of the Banglejs smartwatch
|
||||
if (process.env.HWVERSION == 1){
|
||||
|
|
@ -26,13 +27,13 @@ function setFontSize(pos){
|
|||
}
|
||||
|
||||
function clearField(pos){
|
||||
var yStartPos = Bangle.appRect.y +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos-1) +
|
||||
var yStartPos = Bangle.appRect.y +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos-1) +
|
||||
font6x8At2Size * Math.max(0, pos-2);
|
||||
var yEndPos = Bangle.appRect.y +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos) +
|
||||
var yEndPos = Bangle.appRect.y +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos) +
|
||||
font6x8At2Size * Math.max(0, pos-1);
|
||||
g.clearRect(Bangle.appRect.x, yStartPos, Bangle.appRect.x2, yEndPos);
|
||||
}
|
||||
|
|
@ -44,9 +45,9 @@ function clearWatchIfNeeded(now){
|
|||
|
||||
function drawLine(line, pos){
|
||||
setFontSize(pos);
|
||||
var yPos = Bangle.appRect.y +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos-1) +
|
||||
var yPos = Bangle.appRect.y +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos-1) +
|
||||
font6x8At2Size * Math.max(0, pos-2);
|
||||
g.drawString(line, 5, yPos, true);
|
||||
}
|
||||
|
|
@ -84,6 +85,14 @@ function drawHRM(pos){
|
|||
drawLine(">HR: unknown", pos);
|
||||
}
|
||||
|
||||
function drawAltitude(pos){
|
||||
clearField(pos);
|
||||
if(altitude > 0)
|
||||
drawLine(">Alt: " + altitude.toFixed(1) + "m", pos);
|
||||
else
|
||||
drawLine(">Alt: unknown", pos);
|
||||
}
|
||||
|
||||
function drawActivity(pos){
|
||||
clearField(pos);
|
||||
var health = Bangle.getHealthStatus('last');
|
||||
|
|
@ -104,6 +113,10 @@ function draw(){
|
|||
drawDate(now, curPos);
|
||||
curPos++;
|
||||
}
|
||||
if(settings.showAltitude){
|
||||
drawAltitude(curPos);
|
||||
curPos++;
|
||||
}
|
||||
if(settings.showHRM){
|
||||
drawHRM(curPos);
|
||||
curPos++;
|
||||
|
|
@ -124,6 +137,18 @@ Bangle.on('HRM',function(hrmInfo) {
|
|||
heartRate = hrmInfo.bpm;
|
||||
});
|
||||
|
||||
var MEDIANLENGTH = 20;
|
||||
var avr = [], median;
|
||||
Bangle.on('pressure', function(e) {
|
||||
while (avr.length>MEDIANLENGTH) avr.pop();
|
||||
avr.unshift(e.altitude);
|
||||
median = avr.slice().sort();
|
||||
if (median.length>10) {
|
||||
var mid = median.length>>1;
|
||||
altitude = E.sum(median.slice(mid-4,mid+5)) / 9;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
|
|
@ -135,7 +160,13 @@ var settings = Object.assign({
|
|||
showHRM: true,
|
||||
showActivity: true,
|
||||
showStepCount: true,
|
||||
showAltitude: process.env.HWVERSION != 1 ? true : false,
|
||||
}, require('Storage').readJSON("terminalclock.json", true) || {});
|
||||
|
||||
if(settings.showAltitude && process.env.HWVERSION != 1){
|
||||
Bangle.setBarometerPower(true, "app");
|
||||
}
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
// Load widgets
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Terminal Clock",
|
||||
"shortName":"Terminal Clock",
|
||||
"description": "A terminal cli like clock displaying multiple sensor data",
|
||||
"version":"0.04",
|
||||
"version":"0.05",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
var settings = Object.assign({
|
||||
HRMinConfidence: 50,
|
||||
showDate: true,
|
||||
showAltitude: process.env.HWVERSION != 1 ? true : false,
|
||||
showHRM: true,
|
||||
showActivity: true,
|
||||
showStepCount: true,
|
||||
|
|
@ -14,7 +15,7 @@
|
|||
}
|
||||
|
||||
// Show the menu
|
||||
E.showMenu({
|
||||
var menu = {
|
||||
"" : { "title" : "Terminal Clock" },
|
||||
"< Back" : () => back(),
|
||||
'HR confidence': {
|
||||
|
|
@ -33,6 +34,14 @@
|
|||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show Altitude': {
|
||||
value: settings.showAltitude,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => {
|
||||
settings.showAltitude = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show HRM': {
|
||||
value: settings.showHRM,
|
||||
format: v => v?"Yes":"No",
|
||||
|
|
@ -57,5 +66,9 @@
|
|||
writeSettings();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (process.env.HWVERSION == 1) {
|
||||
delete menu['Show Altitude']
|
||||
}
|
||||
E.showMenu(menu);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Fix positioning
|
||||
0.04: Show GPS fix status
|
||||
0.05: Don't poll for GPS status, override setGPSPower handler (fix #1456)
|
||||
0.06: Periodically update so the always on display does show current GPS fix
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "widgps",
|
||||
"name": "GPS Widget",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "Tiny widget to show the power and fix status of the GPS",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
(function(){
|
||||
var interval;
|
||||
|
||||
// override setGPSPower so we know if GPS is on or off
|
||||
var oldSetGPSPower = Bangle.setGPSPower;
|
||||
Bangle.setGPSPower = function(on,id) {
|
||||
|
|
@ -19,6 +21,16 @@
|
|||
} else {
|
||||
g.setColor("#888"); // off = grey
|
||||
}
|
||||
|
||||
// check if we need to update the widget periodically
|
||||
if (Bangle.isGPSOn() && interval === undefined) {
|
||||
interval = setInterval(function() {
|
||||
WIDGETS.gps.draw(WIDGETS.gps);
|
||||
}, 10*1000); // update every 10 seconds to show gps fix/no fix
|
||||
} else if (!Bangle.isGPSOn() && interval !== undefined) {
|
||||
clearInterval(interval);
|
||||
interval = undefined;
|
||||
}
|
||||
g.drawImage(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), this.x, 2+this.y);
|
||||
}};
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
{ "id": "widslimbat",
|
||||
"name": "Slim battery widget with cells",
|
||||
"shortName":"Slim battery with cells",
|
||||
"version":"0.01",
|
||||
"description": "A small (13px wide) battery widget with cells",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"tags": "widget",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"widslimbat.wid.js","url":"widget.js"}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
(() => {
|
||||
const intervalLow = 60000; // update time when not charging
|
||||
const intervalHigh = 2000; // update time when charging
|
||||
const outline = atob("CRSBAD4AP/AYDAYDAYDAYDAYDAYDAYDAYD/w");
|
||||
|
||||
let COLORS = {
|
||||
'black': g.theme.dark ? "#fff" : "#000",
|
||||
'charging': "#0f0",
|
||||
'low': "#f00",
|
||||
};
|
||||
|
||||
function draw() {
|
||||
var i;
|
||||
var oCol = COLORS.low;
|
||||
var cCol = COLORS.low;
|
||||
var nCells = 0;
|
||||
|
||||
const bat = E.getBattery();
|
||||
if (bat>5) {
|
||||
oCol = COLORS.black;
|
||||
nCells = 1 + Math.floor((bat-6)/19);
|
||||
}
|
||||
if (nCells>1)
|
||||
cCol = COLORS.black;
|
||||
if (Bangle.isCharging())
|
||||
oCol = COLORS.charging;
|
||||
g.reset();
|
||||
g.setColor(oCol).drawImage(outline,this.x+2,this.y+2);
|
||||
for (i=0;i<nCells;i++) {
|
||||
var x = this.x+2+2;
|
||||
var y = this.y+16+2-i*3;
|
||||
g.setColor(cCol).drawRect(x,y,x+4,y+1);
|
||||
}
|
||||
if (Bangle.isCharging()) {
|
||||
changeInterval(id,intervalHigh);
|
||||
} else {
|
||||
changeInterval(id,intervalLow);
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.on("charging",function(charging) {
|
||||
if (charging) Bangle.buzz();
|
||||
WIDGETS["widslimbat"].draw();
|
||||
});
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
WIDGETS["widslimbat"].draw();
|
||||
});
|
||||
var id = setInterval(()=>WIDGETS["widslimbat"].draw(),intervalLow);
|
||||
|
||||
WIDGETS["widslimbat"]={
|
||||
area:"tr",
|
||||
width:13,
|
||||
draw:draw
|
||||
};
|
||||
})();
|
||||
|
After Width: | Height: | Size: 5.6 KiB |
|
|
@ -51,7 +51,8 @@ options is an object containing:
|
|||
* `label` - the text on the button
|
||||
* `cb` - a callback function
|
||||
* `cbl` - a callback function for long presses
|
||||
* `back` - a callback function, passed as `back` into Bangle.setUI
|
||||
* `back` - a callback function, passed as `back` into Bangle.setUI (which usually adds an icon in the top left)
|
||||
|
||||
If automatic lazy rendering is enabled, calls to `layout.render()` will attempt to automatically
|
||||
determine what objects have changed or moved, clear their previous locations, and re-render just those objects.
|
||||
Once `layout.update()` is called, the following fields are added
|
||||
|
|
|
|||
10
package.json
|
|
@ -3,8 +3,13 @@
|
|||
"description": "Bangle.js App Loader (and Apps)",
|
||||
"author": "Gordon Williams <gw@pur3.co.uk> (http://espruino.com)",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/espruino/BangleApps",
|
||||
"devDependencies": {
|
||||
"eslint": "7.1.0"
|
||||
"eslint": "^8.14.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"npm-watch": "^0.11.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint-apps": "eslint ./apps --ext .js",
|
||||
|
|
@ -18,8 +23,5 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"acorn": "^7.2.0"
|
||||
},
|
||||
"devDpendencies": {
|
||||
"npm-watch": "^0.11.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||