Merge branch 'master' into stopw-ms-format

Conflicts:
	apps/clkinfostopw/settings.ts
pull/2697/head
Rob Pilling 2023-04-22 11:58:37 +01:00
commit 0eaa35cfe4
34 changed files with 1073 additions and 70 deletions

View File

@ -31,3 +31,4 @@ clkinfo.addInteractive that would cause ReferenceError.
0.29: use setItem of clockInfoMenu to change the active item
0.30: Use widget_utils
0.31: Use clock_info module as an app
0.32: Make the border of the clock_info box extend all the way to the right of the screen.

View File

@ -135,7 +135,7 @@ let clockInfoMenu = clock_info.addInteractive(clockInfoItems, {
app: "bwclk",
x : 0,
y: 135,
w: W,
w: W+1,
h: H-135,
draw : (itm, info, options) => {
var hideClkInfo = info.text == null;

View File

@ -1,7 +1,7 @@
{
"id": "bwclk",
"name": "BW Clock",
"version": "0.31",
"version": "0.32",
"description": "A very minimalistic clock.",
"readme": "README.md",
"icon": "app.png",

View File

@ -34,3 +34,4 @@ clkinfo.addInteractive that would cause ReferenceError.
0.32: Diverge from BW Clock. Change out the custom font for a standard bitmap one to speed up loading times.
Remove invertion of theme as this doesn'twork very well with fastloading.
Do an quick inital fillRect on theclock info area.
0.33: Make the border of the clock_info box extend all the way to the right of the screen.

View File

@ -95,7 +95,7 @@ let clockInfoMenu = clock_info.addInteractive(clockInfoItems, {
app: "bwclklite",
x : 0,
y: 135,
w: W,
w: W+1,
h: H-135,
draw : (itm, info, options) => {
let hideClkInfo = info.text == null;

View File

@ -1,7 +1,7 @@
{
"id": "bwclklite",
"name": "BW Clock Lite",
"version": "0.32",
"version": "0.33",
"description": "A very minimalistic clock. This version of BW Clock is quicker at the cost of the custom font.",
"readme": "README.md",
"icon": "app.png",

View File

@ -1,4 +1,4 @@
((): ClockInfo.Menu => {
(() => {
let durationOnPause = "---";
let redrawInterval: number | undefined;
let startTime: number | undefined;
@ -80,4 +80,4 @@
}
]
};
})
}) satisfies ClockInfoFunc

View File

@ -0,0 +1,34 @@
const enum StopWatchFormat {
HMS,
Colon,
}
type StopWatchSettings = {
format: StopWatchFormat,
};
(back => {
const SETTINGS_FILE = "clkinfostopw.setting.json";
const storage = require("Storage");
const settings: StopWatchSettings = storage.readJSON(SETTINGS_FILE, true) || {};
settings.format ??= StopWatchFormat.HMS;
const save = () => {
storage.writeJSON(SETTINGS_FILE, settings)
};
E.showMenu({
"": { "title": "stopwatch" },
"< Back": back,
"Format": {
value: settings.format,
min: StopWatchFormat.HMS,
max: StopWatchFormat.Colon,
format: v => v === StopWatchFormat.HMS ? "12m34s" : "12:34",
onchange: v => {
settings.format = v;
save();
},
},
});
}) satisfies SettingsFunc

1
apps/drained/ChangeLog Normal file
View File

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

33
apps/drained/README.md Normal file
View File

@ -0,0 +1,33 @@
# Drained
With this app installed, your Bangle will automatically switch into low power mode when the battery reaches 5% battery (or a preconfigured percentage), displaying a simple clock. When the battery is then charged above 20% (also configurable), normal operation is restored.
Low power mode can also be exited manually, by tapping the primary watch button (an initial tap may be required to unlock the watch).
# Features
## Persistence
- [x] Restore normal operation with sufficient charge
- [x] Reactivate on watch startup
## Time
- [x] Show simple date/time
- [ ] Disable alarms - with a setting?
- [ ] Smarter/backoff interval for checking battery percentage
## No backlight (#2502)
- [x] LCD brightness
- [ ] LCD timeout?
## Peripherals
- [x] Disable auto heart rate measurement in health app (#2502)
- [x] Overwrite setGPSPower() function (#2502)
- [x] Turn off already-running GPS / HRM
## Features
- [x] Wake on twist -> off (#2502)
- [x] Emit `"drained"` event
# Creator
- [bobrippling](https://github.com/bobrippling/)

102
apps/drained/app.js Normal file
View File

@ -0,0 +1,102 @@
var app = "drained";
if (typeof drainedInterval !== "undefined")
drainedInterval = clearInterval(drainedInterval);
Bangle.setLCDBrightness(0);
var powerNoop = function () { return false; };
var forceOff = function (name) {
var _a;
if ((_a = Bangle._PWR) === null || _a === void 0 ? void 0 : _a[name])
Bangle._PWR[name] = [];
Bangle["set".concat(name, "Power")](0, app);
Bangle["set".concat(name, "Power")] = powerNoop;
};
forceOff("GPS");
forceOff("HRM");
try {
NRF.disconnect();
NRF.sleep();
}
catch (e) {
console.log("couldn't disable ble: ".concat(e));
}
Bangle.removeAllListeners();
clearWatch();
Bangle.setOptions({
wakeOnFaceUp: 0,
wakeOnTouch: 0,
wakeOnTwist: 0,
});
var nextDraw;
var draw = function () {
var x = g.getWidth() / 2;
var y = g.getHeight() / 2 - 48;
var date = new Date();
var timeStr = require("locale").time(date, 1);
var dateStr = require("locale").date(date, 0).toUpperCase() +
"\n" +
require("locale").dow(date, 0).toUpperCase();
g.reset()
.clearRect(Bangle.appRect)
.setFont("Vector", 55)
.setFontAlign(0, 0)
.drawString(timeStr, x, y)
.setFont("Vector", 24)
.drawString(dateStr, x, y + 56)
.drawString("".concat(E.getBattery(), "%"), x, y + 104);
if (nextDraw)
clearTimeout(nextDraw);
nextDraw = setTimeout(function () {
nextDraw = undefined;
draw();
}, 60000 - (date.getTime() % 60000));
};
var reload = function () {
Bangle.setUI({
mode: "custom",
remove: function () {
if (nextDraw)
clearTimeout(nextDraw);
nextDraw = undefined;
},
btn: function () {
E.showPrompt("Restore watch to full power?").then(function (v) {
if (v) {
drainedRestore();
}
else {
reload();
}
});
}
});
Bangle.CLOCK = 1;
g.clear();
draw();
};
reload();
Bangle.emit("drained", E.getBattery());
var _a = require("Storage").readJSON("".concat(app, ".setting.json"), true) || {}, _b = _a.disableBoot, disableBoot = _b === void 0 ? false : _b, _c = _a.restore, restore = _c === void 0 ? 20 : _c;
function drainedRestore() {
if (disableBoot) {
try {
eval(require('Storage').read('bootupdate.js'));
}
catch (e) {
console.log("error restoring bootupdate:" + e);
}
}
load();
}
if (disableBoot) {
var checkCharge_1 = function () {
if (E.getBattery() < restore)
return;
drainedRestore();
};
if (Bangle.isCharging())
checkCharge_1();
Bangle.on("charging", function (charging) {
if (charging)
checkCharge_1();
});
}

127
apps/drained/app.ts Normal file
View File

@ -0,0 +1,127 @@
const app = "drained";
// from boot.js
declare var drainedInterval: number | undefined;
if(typeof drainedInterval !== "undefined")
drainedInterval = clearInterval(drainedInterval) as undefined;
// backlight
Bangle.setLCDBrightness(0);
// peripherals
const powerNoop = () => false;
const forceOff = (name: "GPS" | "HRM" | "Compass" /*| "Barom"*/) => {
if ((Bangle as any)._PWR?.[name])
(Bangle as any)._PWR[name] = [];
// if(name === "Barom"){ setBarometerPower(...) }
// ^^^^
Bangle[`set${name}Power`](0, app);
Bangle[`set${name}Power`] = powerNoop;
};
forceOff("GPS");
forceOff("HRM");
try{
NRF.disconnect();
NRF.sleep();
}catch(e){
console.log(`couldn't disable ble: ${e}`);
}
// events
Bangle.removeAllListeners();
clearWatch();
// UI
Bangle.setOptions({
wakeOnFaceUp: 0,
wakeOnTouch: 0,
wakeOnTwist: 0,
});
// clock
let nextDraw: number | undefined;
const draw = () => {
const x = g.getWidth() / 2;
const y = g.getHeight() / 2 - 48;
const date = new Date();
const timeStr = require("locale").time(date, 1);
const dateStr = require("locale").date(date, 0).toUpperCase() +
"\n" +
require("locale").dow(date, 0).toUpperCase();
g.reset()
.clearRect(Bangle.appRect)
.setFont("Vector", 55)
.setFontAlign(0, 0)
.drawString(timeStr, x, y)
.setFont("Vector", 24)
.drawString(dateStr, x, y + 56)
.drawString(`${E.getBattery()}%`, x, y + 104);
if(nextDraw) clearTimeout(nextDraw);
nextDraw = setTimeout(() => {
nextDraw = undefined;
draw();
}, 60000 - (date.getTime() % 60000));
};
const reload = () => {
Bangle.setUI({
mode: "custom",
remove: () => {
if (nextDraw) clearTimeout(nextDraw);
nextDraw = undefined;
},
btn: () => {
E.showPrompt("Restore watch to full power?").then(v => {
if(v){
drainedRestore();
}else{
reload();
}
})
}
});
Bangle.CLOCK=1;
g.clear();
draw();
};
reload();
// permit other apps to put themselves into low-power mode
Bangle.emit("drained", E.getBattery());
// restore normal boot on charge
const { disableBoot = false, restore = 20 }: DrainedSettings
= require("Storage").readJSON(`${app}.setting.json`, true) || {};
// re-enable normal boot code when we're above a threshold:
function drainedRestore() { // "public", to allow users to call
if(disableBoot){
try{
eval(require('Storage').read('bootupdate.js'));
}catch(e){
console.log("error restoring bootupdate:" + e);
}
}
load(); // necessary after updating boot.0
}
if(disableBoot){
const checkCharge = () => {
if(E.getBattery() < restore) return;
drainedRestore();
};
if (Bangle.isCharging())
checkCharge();
Bangle.on("charging", charging => {
if(charging) checkCharge();
});
}

13
apps/drained/boot.js Normal file
View File

@ -0,0 +1,13 @@
{
var _a = require("Storage").readJSON("drained.setting.json", true) || {}, _b = _a.battery, threshold_1 = _b === void 0 ? 5 : _b, _c = _a.interval, interval = _c === void 0 ? 10 : _c, _d = _a.disableBoot, disableBoot_1 = _d === void 0 ? false : _d;
drainedInterval = setInterval(function () {
if (Bangle.isCharging())
return;
if (E.getBattery() > threshold_1)
return;
var app = "drained.app.js";
if (disableBoot_1)
require("Storage").write(".boot0", "if(typeof __FILE__ === \"undefined\" || __FILE__ !== \"".concat(app, "\") setTimeout(load, 100, \"").concat(app, "\");"));
load(app);
}, interval * 60 * 1000);
}

21
apps/drained/boot.ts Normal file
View File

@ -0,0 +1,21 @@
{
const { battery: threshold = 5, interval = 10, disableBoot = false }: DrainedSettings
= require("Storage").readJSON(`drained.setting.json`, true) || {};
drainedInterval = setInterval(() => {
if(Bangle.isCharging())
return;
if(E.getBattery() > threshold)
return;
const app = "drained.app.js";
if(disableBoot)
require("Storage").write(
".boot0",
`if(typeof __FILE__ === "undefined" || __FILE__ !== "${app}") setTimeout(load, 100, "${app}");`
);
load(app);
}, interval * 60 * 1000);
}

BIN
apps/drained/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

View File

@ -0,0 +1,17 @@
{
"id": "drained",
"name": "Drained",
"version": "0.01",
"description": "Switches to displaying a simple clock when the battery percentage is low, and disables some peripherals",
"readme": "README.md",
"icon": "icon.png",
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"allow_emulator": true,
"storage": [
{"name":"drained.boot.js","url":"boot.js"},
{"name":"drained.app.js","url":"app.js"},
{"name":"drained.settings.js","url":"settings.js"}
]
}

58
apps/drained/settings.js Normal file
View File

@ -0,0 +1,58 @@
(function (back) {
var _a, _b, _c, _d;
var SETTINGS_FILE = "drained.setting.json";
var storage = require("Storage");
var settings = storage.readJSON(SETTINGS_FILE, true) || {};
(_a = settings.battery) !== null && _a !== void 0 ? _a : (settings.battery = 5);
(_b = settings.restore) !== null && _b !== void 0 ? _b : (settings.restore = 20);
(_c = settings.interval) !== null && _c !== void 0 ? _c : (settings.interval = 10);
(_d = settings.disableBoot) !== null && _d !== void 0 ? _d : (settings.disableBoot = false);
var save = function () {
storage.writeJSON(SETTINGS_FILE, settings);
};
E.showMenu({
"": { "title": "Drained" },
"< Back": back,
"Keep startup code": {
value: settings.disableBoot,
format: function () { return settings.disableBoot ? "No" : "Yes"; },
onchange: function () {
settings.disableBoot = !settings.disableBoot;
save();
},
},
"Trigger at batt%": {
value: settings.battery,
min: 0,
max: 95,
step: 5,
format: function (v) { return "".concat(v, "%"); },
onchange: function (v) {
settings.battery = v;
save();
},
},
"Poll interval": {
value: settings.interval,
min: 1,
max: 60 * 2,
step: 5,
format: function (v) { return "".concat(v, " mins"); },
onchange: function (v) {
settings.interval = v;
save();
},
},
"Restore watch at %": {
value: settings.restore,
min: 0,
max: 95,
step: 5,
format: function (v) { return "".concat(v, "%"); },
onchange: function (v) {
settings.restore = v;
save();
},
},
});
});

67
apps/drained/settings.ts Normal file
View File

@ -0,0 +1,67 @@
type DrainedSettings = {
battery?: number,
restore?: number,
interval?: number,
disableBoot?: ShortBoolean,
};
(back => {
const SETTINGS_FILE = "drained.setting.json";
const storage = require("Storage")
const settings: DrainedSettings = storage.readJSON(SETTINGS_FILE, true) || {};
settings.battery ??= 5;
settings.restore ??= 20;
settings.interval ??= 10;
settings.disableBoot ??= false;
const save = () => {
storage.writeJSON(SETTINGS_FILE, settings)
};
E.showMenu({
"": { "title": "Drained" },
"< Back": back,
"Keep startup code": {
value: settings.disableBoot,
format: () => settings.disableBoot ? "No" : "Yes",
onchange: () => {
settings.disableBoot = !settings.disableBoot;
save();
},
},
"Trigger at batt%": {
value: settings.battery,
min: 0,
max: 95,
step: 5,
format: (v: number) => `${v}%`,
onchange: (v: number) => {
settings.battery = v;
save();
},
},
"Poll interval": {
value: settings.interval,
min: 1,
max: 60 * 2,
step: 5,
format: (v: number) => `${v} mins`,
onchange: (v: number) => {
settings.interval = v;
save();
},
},
"Restore watch at %": {
value: settings.restore,
min: 0,
max: 95,
step: 5,
format: (v: number) => `${v}%`,
onchange: (v: number) => {
settings.restore = v;
save();
},
},
});
}) satisfies SettingsFunc

View File

@ -1,3 +1,3 @@
0.20: New App!
0.21: Tell clock widgets to hide.
0.22: Changed font so 5 and 6 are not similar

View File

@ -1,13 +1,36 @@
Graphics.prototype.setFontLECO1976Regular42 = function (scale) {
Graphics.prototype.setFontLECO1976Regular5fix42 = function(scale) {
// Actual height 42 (41 - 0)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60 + (scale << 8) + (1 << 16));
this.setFontCustom(
E.toString(require('heatshrink').decompress(atob('ADMD/gHFv/AAwkHB3QAtngGFj47Fh5KFh//BwkH/4OEgf/BwgGCBwcBAwIOEAwQODAwX/wB7CCos/Awo/BAAPgDgvwJwgGEBwX4LoplFAw0P/yCF/4GFh6YGKgQAhNAZGDAwZ4CB3ibCg4ODZoYO/BwyV/BxIA7YX7C/YRRZCAAZZDB2AAgNAMHO4v4O42PB3P4AIL+EwABBQwQO/BwgABBwgGBB34A0h/wAYMDSogDBSogGBUgoOOd/4O2AAbgEAAIO+AGY7C/AHDIIWAB3wQCBwjiDB34OGf1gOdAGbgDgZKFwF/JQn4g4O3/ABBBwmAB34OLcAgOBd4oO6AGY5CJQoADd4gO5f2wOdf1IOdAEgqBA4v//AOGwAO5AwqGCB34OJAAbRCAwbgDB3QAzO/4OL/ABBg4ODwABBv4O/BwyV/BxIAzHYX4gZKFSogOCSowOxf2gOdf1YOdAGkH/EAgY7DSgMASoSWCCIIO3ADg='))),
46,
atob("ERkmICYmJiYmJCYmEQ=="),
60+(scale<<8)+(1<<16)
);
return this;
};
Graphics.prototype.setFontLECO1976Regular22 = function (scale) {
Graphics.prototype.setFontLECO1976Regular5fix22 = function(scale) {
// Actual height 22 (21 - 0)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22 + (scale << 8) + (1 << 16));
this.setFontCustom(
E.toString(require('heatshrink').decompress(atob('AAs8AYV8AaQjOgP8AYMPAYV/AYMH/4DBn///EA///4ADB/wSB//gAYQlCCIIABCIIAFDYIjBAaYjBLYIDTF64AH+CDCGdLLV/i7C/wfCAZ/4/BPCAaTiBAaaHBABaPIIaxPMcbxbBAapgCAahPhVYLDTUbA7CAZ/wv5PKN6xPzAof+AaTuXdcCbuJ8H4ngDCE4QDOJ+8PgBPBh+AE4IDPAA4'))),
46,
atob("CQ0UERQUFBQUExQUCQ=="),
32+(scale<<8)+(1<<16)
);
return this;
};
Graphics.prototype.setFontLECO1976Regular5fix11 = function(scale) {
// Actual height 11 (10 - 0)
this.setFontCustom(
E.toString(require('heatshrink').decompress(atob('AAMBwEDgEGgECgF8g/4v/w/+B+ARBg//h/+j/8mEYsEw//h//D/+AgEMg0Yhk/DofggHAFwMAh9+j38nv4scw41h/nD/OH+YdC5kxzAODxgsBw47CIIM/wF/gAGBhkAjBKFCAN/mH+FgUZw0zhl+jH8mP4CAJZEBwVmBwdj+HwgPggfAQoIxBFgJoDSwUDJQhZDO4QsB4CVB+cP80fNAiVGgaWDmAA=='))),
46,
atob("BAYJCAkJCQkJCQkJBA=="),
15+(scale<<8)+(1<<16)
);
return this;
};
require("Font7x11Numeric7Seg").add(Graphics);
@ -60,7 +83,7 @@ function drawCal() {
const CAL_Y = g.getHeight() - 44; // Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3;
const CAL_AREA_H = 44; // g.getHeight()-CAL_Y+24; //+24: top widgtes only
const CELL_W = g.getWidth() / 7; //cell width
const CELL_H = (CAL_AREA_H - DAY_NAME_FONT_SIZE) / 3; //cell heigth
const CELL_H = 1+parseInt((CAL_AREA_H - DAY_NAME_FONT_SIZE) / 3); //cell heigth
const DAY_NUM_FONT_SIZE = Math.min(CELL_H + 3, 15); //size down, max 15
const wdStrt = 1;
@ -73,11 +96,14 @@ function drawCal() {
const tdyNumClr = 0; // today fg
g.setFont("Vector", DAY_NAME_FONT_SIZE + 3);
g.setColor(nrgb[1]);
g.setFontAlign(-1, -1);
// g.clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H);
//draw grid & Headline
// == draw grid & Headline ==
const dNames = ABR_DAY.map((a) => a.length <= 2 ? a : a.substr(0, 2)); //force shrt 2
for (var dNo = 0; dNo < dNames.length; dNo++) {
const dIdx = wdStrt >= 0 ? ((wdStrt + dNo) % 7) : ((dNo + d.getDay() + 4) % 7);
@ -103,9 +129,10 @@ function drawCal() {
// horizontal lines
// for(i=0; i<3; i++){ const y=nextY+i*CELL_H; g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y); }
g.setFont("Vector", DAY_NUM_FONT_SIZE);
// g.setFont("Vector", DAY_NUM_FONT_SIZE);
g.setFont("7x11Numeric7Seg", 1);
// g.setFont("7x11Numeric7Seg", 1);
g.setFontLECO1976Regular5fix11();
//write days
const tdyDate = d.getDate();
@ -113,16 +140,38 @@ function drawCal() {
var rD = new Date(d.getTime());
rD.setDate(rD.getDate() - days);
var rDate = rD.getDate();
// == today background rectangle ==
for (var y = 0; y < 3; y++) {
for (var x = 0; x < dNames.length; x++) {
if (rDate === tdyDate) { //today
g.setColor(nrgb[tdyMrkClr]); //today marker color or fg color
// rectangle
g.fillRect(x * CELL_W, nextY + CELL_H - 1, x * CELL_W + CELL_W, nextY + CELL_H + CELL_H - 1);
g.setColor(nrgb[tdyNumClr]); //today color or fg color
var frm=3; // fame pixels
g.drawRect(x * CELL_W-frm, nextY + CELL_H - 1-frm, x * CELL_W + CELL_W+frm, nextY + CELL_H + CELL_H - 1+frm);
}
rD.setDate(rDate + 1);
rDate = rD.getDate();
}
}
// == individual days ==
rD = new Date(d.getTime());
rD.setDate(rD.getDate() - days);
rDate = rD.getDate();
for (var y = 0; y < 3; y++) {
for (var x = 0; x < dNames.length; x++) {
if (rDate === tdyDate) { //today
g.setColor(nrgb[tdyMrkClr]); //today marker color or fg color
// rectangle
// g.fillRect(x * CELL_W, nextY + CELL_H - 1, x * CELL_W + CELL_W, nextY + CELL_H + CELL_H - 1);
// g.setColor(nrgb[tdyNumClr]); //today color or fg color
// g.drawRect(x * CELL_W, nextY + CELL_H - 1, x * CELL_W + CELL_W, nextY + CELL_H + CELL_H - 1);
// simulate "bold"
// g.setColor(nrgb[tdyNumClr]); //today color or fg color
g.drawString(rDate, 1 + x * CELL_W + ((CELL_W - g.stringWidth(rDate)) / 2) + 2, nextY + ((CELL_H - DAY_NUM_FONT_SIZE + 2) / 2) + (CELL_H * y));
} else if (IS_SUNDAY[rD.getDay()]) { //sundays
@ -153,7 +202,7 @@ function draw() {
// g.setFont('Vector', 30);
// g.setFont("7x11Numeric7Seg", 5);
g.setFontLECO1976Regular42();
g.setFontLECO1976Regular5fix42();
g.setFontAlign(0, -1);
g.drawString(timeString(h, m), g.getWidth() / 2, 28);
g.drawString(dayString(d), g.getWidth() * 3 / 4, 88);
@ -162,7 +211,7 @@ function draw() {
g.reset();
// Steps
g.setFontLECO1976Regular22();
g.setFontLECO1976Regular5fix22();
g.setFontAlign(-1, -1);
g.drawString(getSteps(), 8, 88);
@ -200,4 +249,4 @@ Bangle.on('lcdPower', on => {
});
Bangle.drawWidgets();
Bangle.drawWidgets();

View File

@ -2,7 +2,7 @@
"id": "glbasic",
"name": "GLBasic Clock",
"shortName": "GLBasic",
"version": "0.21",
"version": "0.22",
"description": "A clock with large numbers",
"dependencies": {"widpedom":"app"},
"icon": "icon48.png",

View File

@ -0,0 +1,2 @@
0.01: New App!
0.02: Display icon of the message origin's app

13
apps/xxlmessage/README.md Normal file
View File

@ -0,0 +1,13 @@
# XXL Message app
This app displays an incomming message with a very large
font and scrolls the text. For people who can't read the
menu font without glasses, this might be an alternative
to the default messages UI app.
When arrived, new messages are displayed instantly on the screen.
When the message fully scrolled two times or
if you touch the screen or press the button,
the default clock is loaded.
Nothing more, nothing less.

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwcA/4A/AH4A/AH2SpMkAQ1/CI3+CI+fB4v5AQQRFk4JB84REBAXkCIgLFEAYsCIgo+BCIwLEC4oRHF4w7CCJAIRGQaDECLQIRLKB9QUKDFRdKB3GQYwAFBwpKFAAhEFAQokHAH4A/AH4A/ABA"))

42
apps/xxlmessage/app.js Normal file
View File

@ -0,0 +1,42 @@

if (require("Storage").read("messagegui")){ // "messagegui" module is installed
require("messages").openGUI();
console.log("Opened Messages UI");
Bangle.load("messagegui");
}
function stop() {
g.setBgColor(0, 1, 1);
g.clear();
g.reset();
load();
}
var txt = 'No Messages';
try{
console.log("try delete messages");
var MESSAGES = require("messages").getMessages();
MESSAGES = [];
txt = 'Deleted all messages';
console.log("worked");
}catch(e){}
g.setBgColor('#ffff00');
g.setColor('#000000');
g.clear();
g.setFont('6x8:3');
g.setFontAlign(0, 0);
g.setColor('#000000');
g.drawString(g.wrapString(txt, g.getWidth()).join("\n"), g.getWidth()/2, g.getHeight()/2);
Bangle.loadWidgets();
Bangle.drawWidgets();
//E.showMessage(txt,{
// title:"XXL Messages",
// img:atob("FBQBAfgAf+Af/4P//D+fx/n+f5/v+f//n//5//+f//n////3//5/n+P//D//wf/4B/4AH4A=") // (i)
//})
setTimeout(stop, 4000);

BIN
apps/xxlmessage/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

1
apps/xxlmessage/boot.js Normal file
View File

@ -0,0 +1 @@
Bangle.on("message", (type, msg) => require("xxlmessage.lib.js").listener(type, msg));

View File

@ -0,0 +1,133 @@
// GB({"t":"notify","id":1575479849,"src":"Hangouts","title":"A Name","body":"message contents"})
var xxl = {
// private:
msg: [],
drawTimeout: undefined,
xpos : 0,
loopCount : 0,
txt:'',
wtot:0,
img:undefined,
imgcol:'#ffffff',
setFont: function(){
g.setFont('6x8:5x9'); // TODO this is a bottleneck. How to prepare the font once?
},
//public:
show: function(theMessage){
console.log("theMessage is:");
console.log(theMessage);
xxl.msg = theMessage;
// prepare string and metrics
xxl.txt = xxl.msg.src + ": " + xxl.msg.body;
xxl.setFont();
xxl.wtot = g.stringMetrics(xxl.txt).width;
xxl.xpos = 2 * g.getWidth();
// get icon
xxl.img = require("messageicons").getImage(xxl.msg);
xxl.imgcol = require("messageicons").getColor(xxl.msg, '#ffffff');
Bangle.loadWidgets();
Bangle.on('touch', function (b, xy) {
xxl.stop();
});
setWatch(xxl.stop, BTN1);
Bangle.buzz(500,1);
xxl.draw();
},
//private:
// schedule a draw for 30 FPS
queueDraw: function() {
if (xxl.drawTimeout) { return; } // clearTimeout(xxl.drawTimeout); }
xxl.drawTimeout = setTimeout(function () {
xxl.drawTimeout = undefined;
xxl.draw();
}, 33 - (Date.now() % 33));
},
stop:function() {
console.log("stop");
if (xxl.drawTimeout) { clearTimeout(xxl.drawTimeout); }
xxl.drawTimeout = undefined;
g.reset();
g.setBgColor('#ffff00');
g.clear();
// Bangle.setLCDPower(0); // light off
// Bangle.setLocked(true); // disable touch
setTimeout(load, 100);
},
draw: function() {
wh = 24; // widgets height
var gw = g.getWidth();
var h = (g.getHeight() - wh)/2; // height of drawing area per stripe
Bangle.setLCDPower(1); // light on
Bangle.setLocked(false); // keep the touch input active
g.setBgColor('#000000');
g.clear();
if (xxl.img) { // 24x24
g.setColor(xxl.imgcol);
g.drawImage(xxl.img
, gw/2, wh+h // center point
,{rotate:0,scale:2}
);
}
xxl.setFont();
g.setFontAlign(-1, -1);
// draw both lines
g.setBgColor('#000000');
g.setColor('#ffffff');
g.drawString(xxl.txt, xxl.xpos, wh);
g.drawString(xxl.txt, xxl.xpos - gw - 32, h + wh);
g.reset();
// widget redraw
Bangle.drawWidgets();
// scroll
xxl.xpos -= 25;
if (xxl.xpos < -xxl.wtot - gw * 2) {
++xxl.loopCount;
if (xxl.loopCount > 2) {
xxl.stop();
return;
}
xxl.xpos = 3 * gw;
}
// loop drawing
xxl.queueDraw();
}
};
// for IDE
// var exports={};
exports.listener = function (type, msg) {
// msg = {t:"add",id:int, src,title,subject,body,sender,tel, important:bool, new:bool}
if (!msg) return;
if (type === 'text' && msg.t !== 'remove') {
msg.handled = true; // don't do anything else with the message
xxl.show(msg);
}
};
// debug
// Bangle.on("message", (type, msg) => exports.listener(type, msg));

194
apps/xxlmessage/lib.js Normal file
View File

@ -0,0 +1,194 @@
// GB({t:"notify",id:1680248072,src:"SMS Messenger",title:"Fabia",body:"Nein"})
// msg = {"t":"add","id":1680248072,"src":"SMS Messenger","title":"Fabia","body":"Nein","new":true,"handled":true}
var xxl = {
// private:
msg: [],
drawTimeout: undefined,
xpos : 0,
loopCount : 0,
txt:'',
wtot:0,
img:undefined,
imgcol:'#ffffff',
// gfx buffer
bufimg:undefined,
bufpal4color:undefined,
buffnt:'6x15', // font to use. Built-in: 4x6, 6x8,12x20,6x15,Vector
bufw:0, // width of buffer for all lines
bufh:0, // height of buffer
buflin:0, // number of lines to print
bufscale:0, // scale factor for buffer to screen
// public:
show: function(theMessage){
// console.log("theMessage is:");
// console.log(theMessage);
xxl.msg = theMessage;
// get icon
try{
xxl.img = require("messageicons").getImage(xxl.msg);
xxl.imgcol = (require("messageicons").getColor(xxl.msg, '#ffffff')||'#00ffff');
}catch(e){}
Bangle.loadWidgets();
Bangle.on('touch', function (b, xy) {
xxl.stop();
});
setWatch(xxl.stop, BTN1);
Bangle.buzz(500,1);
// offscreen gfx buffer
// screen is 176x176
// font should be scaled 5x9=30x72px
// built in fonts are 4x6, 6x8,12x20,6x15,Vector
xxl.bufpal4color = new Uint16Array([0x0000,0xFFFF,0x7BEF,0xAFE5],0,2); // b,w,grey,greenyellow
g.setFont(xxl.buffnt);
var hfont = g.getFontHeight();
xxl.bufscale=parseInt((g.getHeight() - 24/*widgets*/)/2) / hfont;
xxl.buflin=2; // number of lines
xxl.bufw=(g.getWidth() * xxl.buflin) / xxl.bufscale; // 6x15 font scaled by 5 on 176 screen width
xxl.bufh=hfont;
xxl.bufimg = Graphics.createArrayBuffer(xxl.bufw,xxl.bufh,2,{msb:true});
// prepare string and metrics
xxl.txt = (xxl.msg.title||(xxl.msg.src||"MSG")) + ": " + (xxl.msg.body||"-x-");
g.setFont(xxl.buffnt);
xxl.wtot = g.stringMetrics(xxl.txt).width;
xxl.xpos = xxl.bufw; // g.getWidth();
xxl.draw();
},
//private:
// schedule a draw for 60 FPS
queueDraw: function() {
if (xxl.drawTimeout) { return; } // clearTimeout(xxl.drawTimeout); }
xxl.drawTimeout = setTimeout(function () {
xxl.drawTimeout = undefined;
xxl.draw();
}, 16 - (Date.now() % 16));
},
stop:function() {
// console.log("stop");
if (xxl.drawTimeout) { clearTimeout(xxl.drawTimeout); }
xxl.drawTimeout = undefined;
g.reset();
g.setBgColor('#ffff00');
g.clear();
// Bangle.setLCDPower(0); // light off
// Bangle.setLocked(true); // disable touch
setTimeout(function(){Bangle.showClock();}, 100);
},
// this is even slower than the scaled printing :(
// megaPrintBufferd: function(txt, x, y){
// xxl.bufimg.setFont(xxl.buffnt);
// xxl.bufimg.setFontAlign(-1, -1);
// xxl.bufimg.setColor(1); // index in palette
// xxl.bufimg.clear();
// xxl.bufimg.drawString(txt, x, 0);
// for(var i = 0; i<xxl.buflin; ++i){
// g.drawImage({
// width:xxl.bufw, height:xxl.bufh, bpp:2
// , buffer: xxl.bufimg.buffer
// , palette: xxl.bufpal4color
// }
// , -i*g.getWidth(), y
// ,{scale:xxl.bufscale}
// );
// y+=xxl.bufscale*xxl.bufh;
// }
// },
// x: pixels in buffer. Must scale this.
// y: screen position
megaPrint: function(txt, x, y){
g.setFont(xxl.buffnt+':'+xxl.bufscale);
g.setColor('#ffffff');
g.setFontAlign(-1, -1);
for(var i = 0; i<xxl.buflin; ++i){
g.drawString(txt
, x*xxl.bufscale-i*g.getWidth()
, y
);
y+=xxl.bufscale*xxl.bufh;
}
},
draw: function() {
var wh = 24; // widgets height
var gw = g.getWidth();
var h = (g.getHeight() - wh)/2; // height of drawing area per stripe
Bangle.setLCDPower(1); // light on
Bangle.setLocked(false); // keep the touch input active
g.setBgColor('#000000');
g.clear();
// center line
g.setColor(xxl.imgcol);
g.fillRect( 0,wh+h-3,gw/2-26,wh+h-1);
g.fillRect(gw/2+26,wh+h-3, gw-1,wh+h-1);
// image
if (xxl.img) { // 24x24
g.setColor(xxl.imgcol);
g.drawImage(xxl.img
, gw/2, wh+h-2 // center point
,{rotate:0,scale:2}
);
}
// scroll message
xxl.megaPrint(xxl.txt, xxl.xpos, wh);
g.reset();
// widget redraw
Bangle.drawWidgets();
// scroll
xxl.xpos -= 3; // buffer pixels
if (xxl.xpos < -xxl.wtot - xxl.bufw/xxl.buflin - 4) {
++xxl.loopCount;
if (xxl.loopCount > 2) {
xxl.stop();
return;
}
xxl.xpos = (3*xxl.bufw)/2;
}
// loop drawing
xxl.queueDraw();
}
};
// for IDE
// var exports={};
exports.listener = function (type, msg) {
// msg = {t:"add",id:int, src,title,subject,body,sender,tel, important:bool, new:bool}
if (!msg) return;
if (type === 'text' && msg.t !== 'remove') {
msg.handled = true; // don't do anything else with the message
xxl.show(msg);
}
};
// debug
// var msg = {t:"add",id:12341, src:"SMS",title:undefined,subject:undefined,body:"yes",sender:"phoo",tel:undefined, important:false, new:true};
// exports.listener('text', msg);

View File

@ -0,0 +1,20 @@
{
"id": "xxlmessage",
"name": "XXL Message",
"version": "0.02",
"shortName": "XXL Msg",
"description": "App to display large notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",
"tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : {"messages":"module", "messageicons":"module" },
"readme": "README.md",
"screenshots": [{"url":"screenshot.png"}],
"storage": [
{"name":"xxlmessage.app.js","url":"app.js"},
{"name":"xxlmessage.lib.js","url":"lib.js"},
{"name":"xxlmessage.boot.js","url":"boot.js"},
{"name":"xxlmessage.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -16,7 +16,7 @@ if (window.location.host=="banglejs.com") {
'This is not the official Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.';
}
var RECOMMENDED_VERSION = "2v16";
var RECOMMENDED_VERSION = "2v17";
// could check http://www.espruino.com/json/BANGLEJS.json for this
// We're only interested in Bangles

2
typescript/types/funcs.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
type SettingsFunc = (back: () => void) => void;
type ClockInfoFunc = () => ClockInfo.Menu;

View File

@ -90,6 +90,8 @@ type Widget = {
};
declare const WIDGETS: { [key: string]: Widget };
type ShortBoolean = boolean | 0 | 1;
type AccelData = {
x: number;
y: number;
@ -153,13 +155,13 @@ type LCDMode =
| "120x120"
| "80x80"
type BangleOptions = {
wakeOnBTN1: boolean;
wakeOnBTN2: boolean;
wakeOnBTN3: boolean;
wakeOnFaceUp: boolean;
wakeOnTouch: boolean;
wakeOnTwist: boolean;
type BangleOptions<Boolean = boolean> = {
wakeOnBTN1: Boolean;
wakeOnBTN2: Boolean;
wakeOnBTN3: Boolean;
wakeOnFaceUp: Boolean;
wakeOnTouch: Boolean;
wakeOnTwist: Boolean;
twistThreshold: number;
twistMaxY: number;
twistTimeout: number;
@ -174,6 +176,12 @@ type BangleOptions = {
btnLoadTimeout: number;
};
type SetUIArg<Mode> = Mode | {
mode: Mode,
back?: () => void,
remove?: () => void,
};
type NRFFilters = {
services?: string[];
name?: string;
@ -296,6 +304,12 @@ type VariableSizeInformation = {
more?: VariableSizeInformation;
};
type PipeOptions = {
chunkSize?: number,
end?: boolean,
complete?: () => void,
};
// CLASSES
@ -794,6 +808,29 @@ declare class NRF {
*/
static on(event: "security", callback: (status: any) => void): void;
/**
* Called when Bluetooth advertising starts or stops on Espruino
* @param {string} event - The event to listen to.
* @param {(isAdvertising: boolean) => void} callback - A function that is executed when the event occurs. Its arguments are:
* * `isAdvertising` Whether we are advertising or not
* @url http://www.espruino.com/Reference#l_NRF_advertising
*/
static on(event: "advertising", callback: (isAdvertising: boolean) => void): void;
/**
* Called during the bonding process to update on status
* `status` is one of:
* * `"request"` - Bonding has been requested in code via `NRF.startBonding`
* * `"start"` - The bonding procedure has started
* * `"success"` - The bonding procedure has succeeded (`NRF.startBonding`'s promise resolves)
* * `"fail"` - The bonding procedure has failed (`NRF.startBonding`'s promise rejects)
* @param {string} event - The event to listen to.
* @param {(status: any) => void} callback - A function that is executed when the event occurs. Its arguments are:
* * `status` One of `'request'/'start'/'success'/'fail'`
* @url http://www.espruino.com/Reference#l_NRF_bond
*/
static on(event: "bond", callback: (status: any) => void): void;
/**
* Called with a single byte value when Espruino is set up as a HID device and the
* computer it is connected to sends a HID report back to Espruino. This is usually
@ -985,15 +1022,17 @@ declare class NRF {
* `options` is an object, which can contain:
* ```
* {
* name: "Hello" // The name of the device
* showName: true/false // include full name, or nothing
* discoverable: true/false // general discoverable, or limited - default is limited
* connectable: true/false // whether device is connectable - default is true
* scannable : true/false // whether device can be scanned for scan response packets - default is true
* interval: 600 // Advertising interval in msec, between 20 and 10000 (default is 375ms)
* manufacturer: 0x0590 // IF sending manufacturer data, this is the manufacturer ID
* manufacturerData: [...] // IF sending manufacturer data, this is an array of data
* phy: "1mbps/2mbps/coded" // (NRF52840 only) use the long-range coded phy for transmission (1mbps default)
* name: "Hello" // The name of the device
* showName: true/false // include full name, or nothing
* discoverable: true/false // general discoverable, or limited - default is limited
* connectable: true/false // whether device is connectable - default is true
* scannable : true/false // whether device can be scanned for scan response packets - default is true
* whenConnected : true/false // keep advertising when connected (nRF52 only)
* // switches to advertising as non-connectable when it is connected
* interval: 600 // Advertising interval in msec, between 20 and 10000 (default is 375ms)
* manufacturer: 0x0590 // IF sending manufacturer data, this is the manufacturer ID
* manufacturerData: [...] // IF sending manufacturer data, this is an array of data
* phy: "1mbps/2mbps/coded" // (NRF52840 only) use the long-range coded phy for transmission (1mbps default)
* }
* ```
* Setting `connectable` and `scannable` to false gives the lowest power
@ -2274,7 +2313,7 @@ declare class Socket {
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_Socket_pipe
*/
pipe(destination: any, options?: any): void;
pipe(destination: any, options?: PipeOptions): void
/**
* This function writes the `data` argument as a string. Data that is passed in
@ -2469,7 +2508,7 @@ declare class httpSRq {
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_httpSRq_pipe
*/
pipe(destination: any, options?: any): void;
pipe(dest: any, options?: PipeOptions): void
}
/**
@ -2692,7 +2731,7 @@ declare class httpCRs {
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_httpCRs_pipe
*/
pipe(destination: any, options?: any): void;
pipe(destination: any, options?: PipeOptions): void
}
/**
@ -2906,20 +2945,11 @@ declare class Microbit {
}
/**
* This is the File object - it allows you to stream data to and from files (As
* opposed to the `require('fs').readFile(..)` style functions that read an entire
* file).
* To create a File object, you must type ```var fd =
* E.openFile('filepath','mode')``` - see [E.openFile](#l_E_openFile) for more
* information.
* **Note:** If you want to remove an SD card after you have started using it, you
* *must* call `E.unmountSD()` or you may cause damage to the card.
* @url http://www.espruino.com/Reference#File
*/
declare class File {
interface FileConstructor {
}
interface File {
/**
* Close an open file.
* @url http://www.espruino.com/Reference#l_File_close
@ -2976,9 +3006,22 @@ declare class File {
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_File_pipe
*/
pipe(destination: any, options?: any): void;
pipe(destination: any, options?: PipeOptions): void
}
/**
* This is the File object - it allows you to stream data to and from files (As
* opposed to the `require('fs').readFile(..)` style functions that read an entire
* file).
* To create a File object, you must type ```var fd =
* E.openFile('filepath','mode')``` - see [E.openFile](#l_E_openFile) for more
* information.
* **Note:** If you want to remove an SD card after you have started using it, you
* *must* call `E.unmountSD()` or you may cause damage to the card.
* @url http://www.espruino.com/Reference#File
*/
declare const File: FileConstructor
/**
* Class containing [Puck.js's](http://www.puck-js.com) utility functions.
* @url http://www.espruino.com/Reference#Puck
@ -3519,7 +3562,7 @@ declare class Bangle {
* @param {string} event - The event to listen to.
* @param {(button: number, xy: any) => void} callback - A function that is executed when the event occurs. Its arguments are:
* * `button` `1` for left, `2` for right
* * `xy` Object of form `{x,y}` containing touch coordinates (if the device supports full touch). Clipped to 0..175 (LCD pixel coordinates) on firmware 2v13 and later.
* * `xy` Object of form `{x,y,type}` containing touch coordinates (if the device supports full touch). Clipped to 0..175 (LCD pixel coordinates) on firmware 2v13 and later.`type` is only available on Bangle.js 2 and is an integer, either 0 for swift touches or 2 for longer ones.
* @url http://www.espruino.com/Reference#l_Bangle_touch
*/
static on(event: "touch", callback: TouchCallback): void;
@ -3783,7 +3826,7 @@ declare class Bangle {
* @param {any} options
* @url http://www.espruino.com/Reference#l_Bangle_setOptions
*/
static setOptions(options: { [key in keyof BangleOptions]?: BangleOptions[key] }): void;
static setOptions(options: { [key in keyof BangleOptions]?: BangleOptions<ShortBoolean>[key] }): void;
/**
* Return the current state of options as set by `Bangle.setOptions`
@ -3844,7 +3887,7 @@ declare class Bangle {
* @returns {boolean} Is HRM on?
* @url http://www.espruino.com/Reference#l_Bangle_setHRMPower
*/
static setHRMPower(isOn: boolean, appID: string): boolean;
static setHRMPower(isOn: ShortBoolean, appID: string): boolean;
/**
* Is the Heart rate monitor powered?
@ -3868,7 +3911,7 @@ declare class Bangle {
* @returns {boolean} Is the GPS on?
* @url http://www.espruino.com/Reference#l_Bangle_setGPSPower
*/
static setGPSPower(isOn: boolean, appID: string): boolean;
static setGPSPower(isOn: ShortBoolean, appID: string): boolean;
/**
* Is the GPS powered?
@ -3900,7 +3943,7 @@ declare class Bangle {
* @returns {boolean} Is the Compass on?
* @url http://www.espruino.com/Reference#l_Bangle_setCompassPower
*/
static setCompassPower(isOn: boolean, appID: string): boolean;
static setCompassPower(isOn: ShortBoolean, appID: string): boolean;
/**
* Is the compass powered?
@ -3928,7 +3971,7 @@ declare class Bangle {
* @returns {boolean} Is the Barometer on?
* @url http://www.espruino.com/Reference#l_Bangle_setBarometerPower
*/
static setBarometerPower(isOn: boolean, appID: string): boolean;
static setBarometerPower(isOn: ShortBoolean, appID: string): boolean;
/**
* Is the Barometer powered?
@ -4294,7 +4337,11 @@ declare class Bangle {
* @param {any} callback - A function with one argument which is the direction
* @url http://www.espruino.com/Reference#l_Bangle_setUI
*/
static setUI(type?: "updown" | "leftright" | "clock" | "clockupdown" | { mode: "custom"; back?: () => void; touch?: TouchCallback; swipe?: SwipeCallback; drag?: DragCallback; btn?: (n: number) => void, remove?: () => void, clock?: boolean }, callback?: (direction?: -1 | 1) => void): void;
static setUI(type?: undefined): void;
static setUI(type: SetUIArg<"updown" | "leftright">, callback: (direction?: -1 | 1) => void): void;
static setUI(type: SetUIArg<"clock">): void;
static setUI(type: SetUIArg<"clockupdown">, callback?: (direction: -1 | 1) => void): void;
static setUI(type: SetUIArg<"custom"> & { touch?: TouchCallback; swipe?: SwipeCallback; drag?: DragCallback; btn?: (n: 1 | 2 | 3) => void; clock?: boolean | 0 | 1 }): void;
/**
* @url http://www.espruino.com/Reference#l_Bangle_setUI
@ -4316,7 +4363,7 @@ declare class Bangle {
*/
static appRect: { x: number, y: number, w: number, h: number, x2: number, y2: number };
static CLOCK: boolean;
static CLOCK: ShortBoolean;
static strokes: undefined | { [key: string]: Unistroke };
}
@ -7972,7 +8019,7 @@ declare class E {
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_E_pipe
*/
static pipe(source: any, destination: any, options?: { chunkSize?: number, end?: boolean, complete?: () => void }): void
static pipe(source: any, destination: any, options?: PipeOptions): void
/**
* Create an ArrayBuffer from the given string. This is done via a reference, not a
@ -8609,6 +8656,12 @@ declare class E {
/**
* This class provides a software-defined OneWire master. It is designed to be
* similar to Arduino's OneWire library.
* **Note:** OneWire commands are very timing-sensitive, and on nRF52 devices
* (Bluetooth LE Espruino boards) the bluetooth stack can get in the way. Before
* version 2v18 of Espruino OneWire could be unreliable, but as of firmware 2v18
* Espruino now schedules OneWire accesses with the bluetooth stack to ensure it doesn't interfere.
* OneWire is now reliable but some functions such as `OneWire.search` can now take
* a while to execute (around 1 second).
* @url http://www.espruino.com/Reference#OneWire
*/
declare class OneWire {
@ -8946,10 +8999,10 @@ interface Object {
* ```
* For more information see `Object.on`
*
* @param {any} event - The name of the event, for instance `'data'`. If not specified *all* listeners are removed.
* @param {any} [event] - [optional] The name of the event, for instance `'data'`. If not specified *all* listeners are removed.
* @url http://www.espruino.com/Reference#l_Object_removeAllListeners
*/
removeAllListeners(event: any): void;
removeAllListeners(event?: any): void;
}
/**
@ -9668,6 +9721,19 @@ declare class StorageFile {
* @url http://www.espruino.com/Reference#l_StorageFile_erase
*/
erase(): void;
/**
* Pipe this file to a stream (an object with a 'write' method)
*
* @param {any} destination - The destination file/stream that will receive content from the source.
* @param {any} [options]
* [optional] An object `{ chunkSize : int=32, end : bool=true, complete : function }`
* chunkSize : The amount of data to pipe from source to destination at a time
* complete : a function to call when the pipe activity is complete
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_StorageFile_pipe
*/
pipe(destination: any, options?: PipeOptions): void
}
interface processConstructor {
@ -10002,7 +10068,7 @@ declare class Serial {
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_Serial_pipe
*/
pipe(destination: any, options?: any): void;
pipe(destination: any, options?: PipeOptions): void
}
interface StringConstructor {
@ -11437,10 +11503,11 @@ declare function setWatch(func: ((arg: { state: boolean, time: number, lastTime:
* Clear the Watch that was created with setWatch. If no parameter is supplied, all watches will be removed.
* To avoid accidentally deleting all Watches, if a parameter is supplied but is `undefined` then an Exception will be thrown.
*
* @param {any} id - The id returned by a previous call to setWatch. **Only one argument is allowed.**
* @param {any} id - The id returned by a previous call to setWatch. **Only one argument is allowed.** (or pass nothing to clear all watches)
* @url http://www.espruino.com/Reference#l__global_clearWatch
*/
declare function clearWatch(id: number): void;
declare function clearWatch(): void;
/**
* A variable containing the arguments given to the function:
@ -11991,6 +12058,10 @@ declare module "neopixel" {
* white). These are still supported but the array of data supplied must still be a
* multiple of 3 bytes long. Just round the size up - it won't cause any problems.
* * On some platforms like STM32, pins capable of hardware SPI MOSI are required.
* * On STM32, `neopixel.write` chooses a hardware SPI device to output the signal on
* and uses that. However in order to avoid spikes in the output, if that hardware device is *already
* initialised* it will not be re-initialised. This means that if the SPI device was already in use,
* you may have to use `SPIx.setup({baud:3200000, mosi:the_pin})` to force it to be re-setup on the pin.
* * Espruino devices tend to have 3.3v IO, while WS2812/etc run off of 5v. Many
* WS2812 will only register a logic '1' at 70% of their input voltage - so if
* powering them off 5v you will not be able to send them data reliably. You can
@ -12915,7 +12986,7 @@ declare module "fs" {
* end : call the 'end' function on the destination when the source is finished
* @url http://www.espruino.com/Reference#l_fs_pipe
*/
function pipe(source: any, destination: any, options?: any): void;
function pipe(destination: any, options?: PipeOptions): void
}
/**
@ -13272,7 +13343,7 @@ declare module "Storage" {
/**
* The Flash Storage system is journaling. To make the most of the limited write
* cycles of Flash memory, Espruino marks deleted/replaced files as garbage and
* cycles of Flash memory, Espruino marks deleted/replaced files as garbage/trash files and
* moves on to a fresh part of flash memory. Espruino only fully erases those files
* when it is running low on flash, or when `compact` is called.
* `compact` may fail if there isn't enough RAM free on the stack to use as swap
@ -13311,7 +13382,7 @@ declare module "Storage" {
* fileBytes // How many bytes of allocated files do we have?
* fileCount // How many allocated files do we have?
* trashBytes // How many bytes of trash files do we have?
* trashCount // How many trash files do we have?
* trashCount // How many trash files do we have? (can be cleared with .compact)
* }
* ```
* @returns {any} An object containing info about the current Storage system