Merge remote-tracking branch 'upstream/master'
commit
cdfe284987
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Design improvements and fixes.
|
||||
0.03: Indicate battery level through line occurrence.
|
||||
0.04: Use widget_utils module.
|
||||
0.05: Support for clkinfo.
|
||||
|
|
@ -10,7 +10,9 @@ The original output of stable diffusion is shown here:
|
|||
|
||||
My implementation is shown below. Note that horizontal lines occur randomly, but the
|
||||
probability is correlated with the battery level. So if your screen contains only
|
||||
a few lines its time to charge your bangle again ;)
|
||||
a few lines its time to charge your bangle again ;) Also note that the upper text
|
||||
implementes the clkinfo module and can be configured via touch left/right/up/down.
|
||||
Touch at the center to trigger the selected action.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
/**
|
||||
/************************************************
|
||||
* AI Clock
|
||||
*/
|
||||
const storage = require('Storage');
|
||||
const clock_info = require("clock_info");
|
||||
|
||||
|
||||
|
||||
/************************************************
|
||||
* Assets
|
||||
*/
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
Graphics.prototype.setFontGochiHand = function(scale) {
|
||||
// Actual height 27 (29 - 3)
|
||||
|
|
@ -13,7 +21,7 @@ Graphics.prototype.setFontGochiHand = function(scale) {
|
|||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
/************************************************
|
||||
* Set some important constants such as width, height and center
|
||||
*/
|
||||
var W = g.getWidth(),R=W/2;
|
||||
|
|
@ -21,6 +29,120 @@ var H = g.getHeight();
|
|||
var cx = W/2;
|
||||
var cy = H/2;
|
||||
var drawTimeout;
|
||||
var lock_input = false;
|
||||
|
||||
|
||||
/************************************************
|
||||
* SETTINGS
|
||||
*/
|
||||
const SETTINGS_FILE = "aiclock.setting.json";
|
||||
let settings = {
|
||||
menuPosX: 0,
|
||||
menuPosY: 0,
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
settings[key] = saved_settings[key]
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
* Menu
|
||||
*/
|
||||
function getDate(){
|
||||
var date = new Date();
|
||||
return ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2)
|
||||
}
|
||||
|
||||
|
||||
// Custom clockItems menu - therefore, its added here and not in a clkinfo.js file.
|
||||
var clockItems = {
|
||||
name: getDate(),
|
||||
img: null,
|
||||
items: [
|
||||
{ name: "Week",
|
||||
get: () => ({ text: "Week " + weekOfYear(), img: null}),
|
||||
show: function() { clockItems.items[0].emit("redraw"); },
|
||||
hide: function () {}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
function weekOfYear() {
|
||||
var date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
// Thursday in current week decides the year.
|
||||
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
|
||||
// January 4 is always in week 1.
|
||||
var week1 = new Date(date.getFullYear(), 0, 4);
|
||||
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
||||
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
|
||||
- 3 + (week1.getDay() + 6) % 7) / 7);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Load menu
|
||||
var menu = clock_info.load();
|
||||
menu = menu.concat(clockItems);
|
||||
|
||||
|
||||
// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it.
|
||||
if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){
|
||||
settings.menuPosX = 0;
|
||||
settings.menuPosY = 0;
|
||||
}
|
||||
|
||||
// Set draw functions for each item
|
||||
menu.forEach((menuItm, x) => {
|
||||
menuItm.items.forEach((item, y) => {
|
||||
function drawItem() {
|
||||
// For the clock, we have a special case, as we don't wanna redraw
|
||||
// immediately when something changes. Instead, we update data each minute
|
||||
// to save some battery etc. Therefore, we hide (and disable the listener)
|
||||
// immedeately after redraw...
|
||||
item.hide();
|
||||
|
||||
// After drawing the item, we enable inputs again...
|
||||
lock_input = false;
|
||||
|
||||
var info = item.get();
|
||||
drawMenuItem(info.text, info.img);
|
||||
}
|
||||
|
||||
item.on('redraw', drawItem);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
function canRunMenuItem(){
|
||||
if(settings.menuPosY == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
var menuEntry = menu[settings.menuPosX];
|
||||
var item = menuEntry.items[settings.menuPosY-1];
|
||||
return item.run !== undefined;
|
||||
}
|
||||
|
||||
|
||||
function runMenuItem(){
|
||||
if(settings.menuPosY == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
var menuEntry = menu[settings.menuPosX];
|
||||
var item = menuEntry.items[settings.menuPosY-1];
|
||||
try{
|
||||
var ret = item.run();
|
||||
if(ret){
|
||||
Bangle.buzz(300, 0.6);
|
||||
}
|
||||
} catch (ex) {
|
||||
// Simply ignore it...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/
|
||||
|
|
@ -76,7 +198,50 @@ function toAngle(a){
|
|||
return a
|
||||
}
|
||||
|
||||
|
||||
function drawMenuItem(text, image){
|
||||
if(text == null){
|
||||
drawTime();
|
||||
return
|
||||
}
|
||||
// image = atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==");
|
||||
|
||||
text = String(text);
|
||||
|
||||
g.reset().setBgColor("#fff").setColor("#000");
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("Vector", 20);
|
||||
|
||||
var imgWidth = image == null ? 0 : 24;
|
||||
var strWidth = g.stringWidth(text);
|
||||
var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2);
|
||||
var w = imgWidth + strWidth;
|
||||
|
||||
g.clearRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2)
|
||||
|
||||
// Draw right line as designed by stable diffusion
|
||||
g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2);
|
||||
g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2);
|
||||
g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2);
|
||||
|
||||
// And finally the text
|
||||
g.drawString(text, cx+imgWidth/2, 42);
|
||||
g.drawString(text, cx+1+imgWidth/2, 41);
|
||||
|
||||
if(image != null) {
|
||||
var scale = image.width ? imgWidth / image.width : 1;
|
||||
g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale});
|
||||
}
|
||||
|
||||
drawTime();
|
||||
}
|
||||
|
||||
|
||||
function drawTime(){
|
||||
// Draw digital time first
|
||||
drawDigits();
|
||||
|
||||
// And now the analog time
|
||||
var drawHourHand = g.drawRotRect.bind(g,8,12,R-38);
|
||||
var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 );
|
||||
|
||||
|
|
@ -90,13 +255,6 @@ function drawTime(){
|
|||
h += date.getMinutes()/60.0;
|
||||
h = parseInt(h*360/12);
|
||||
|
||||
// Draw minute and hour bg
|
||||
g.setColor(g.theme.bg);
|
||||
drawHourHand(toAngle(h-3));
|
||||
drawHourHand(toAngle(h+3));
|
||||
drawMinuteHand(toAngle(m-2));
|
||||
drawMinuteHand(toAngle(m+3));
|
||||
|
||||
// Draw minute and hour fg
|
||||
g.setColor(g.theme.fg);
|
||||
drawHourHand(h);
|
||||
|
|
@ -104,28 +262,6 @@ function drawTime(){
|
|||
}
|
||||
|
||||
|
||||
|
||||
function drawDate(){
|
||||
var date = new Date();
|
||||
g.setFontAlign(0,0);
|
||||
g.setFontGochiHand();
|
||||
|
||||
var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2);
|
||||
var w = g.stringWidth(text);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12);
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
// Draw right line as designed by stable diffusion
|
||||
g.drawLine(cx+w/2+5, 20, cx+w/2+5, 40+12);
|
||||
g.drawLine(cx+w/2+6, 20, cx+w/2+6, 40+12);
|
||||
g.drawLine(cx+w/2+7, 20, cx+w/2+7, 40+12);
|
||||
|
||||
// And finally the text
|
||||
g.drawString(text, cx, 40);
|
||||
}
|
||||
|
||||
|
||||
function drawDigits(){
|
||||
var date = new Date();
|
||||
|
||||
|
|
@ -156,20 +292,35 @@ function drawDigits(){
|
|||
}
|
||||
|
||||
|
||||
function drawDate(){
|
||||
var menuEntry = menu[settings.menuPosX];
|
||||
|
||||
// The first entry is the overview...
|
||||
if(settings.menuPosY == 0){
|
||||
drawMenuItem(menuEntry.name, menuEntry.img);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw item if needed
|
||||
lock_input = true;
|
||||
var item = menuEntry.items[settings.menuPosY-1];
|
||||
item.show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function draw(){
|
||||
// Queue draw in one minute
|
||||
queueDraw();
|
||||
|
||||
|
||||
g.reset();
|
||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||
|
||||
g.setColor(1,1,1);
|
||||
|
||||
drawBackground();
|
||||
drawDate();
|
||||
drawDigits();
|
||||
drawTime();
|
||||
drawCircle(Bangle.isLocked());
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +341,68 @@ Bangle.on('lock', function(isLocked) {
|
|||
drawCircle(isLocked);
|
||||
});
|
||||
|
||||
Bangle.on('touch', function(btn, e){
|
||||
var left = parseInt(g.getWidth() * 0.22);
|
||||
var right = g.getWidth() - left;
|
||||
var upper = parseInt(g.getHeight() * 0.22);
|
||||
var lower = g.getHeight() - upper;
|
||||
|
||||
var is_upper = e.y < upper;
|
||||
var is_lower = e.y > lower;
|
||||
var is_left = e.x < left && !is_upper && !is_lower;
|
||||
var is_right = e.x > right && !is_upper && !is_lower;
|
||||
var is_center = !is_upper && !is_lower && !is_left && !is_right;
|
||||
|
||||
if(lock_input){
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_lower){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
if(is_upper){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY = settings.menuPosY-1;
|
||||
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY;
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
if(is_right){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosX = (settings.menuPosX+1) % menu.length;
|
||||
settings.menuPosY = 0;
|
||||
draw();
|
||||
}
|
||||
|
||||
if(is_left){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY = 0;
|
||||
settings.menuPosX = settings.menuPosX-1;
|
||||
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
|
||||
draw();
|
||||
}
|
||||
|
||||
if(is_center){
|
||||
if(canRunMenuItem()){
|
||||
runMenuItem();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
E.on("kill", function(){
|
||||
try{
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
} catch(ex){
|
||||
// If this fails, we still kill the app...
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Some helpers
|
||||
|
|
@ -203,7 +416,6 @@ function queueDraw() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lets start widgets, listen for btn etc.
|
||||
*/
|
||||
|
|
@ -216,6 +428,7 @@ Bangle.loadWidgets();
|
|||
* area to the top bar doesn't get cleared.
|
||||
*/
|
||||
require('widget_utils').hide();
|
||||
|
||||
// Clear the screen once, at startup and draw clock
|
||||
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
|
||||
draw();
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -3,7 +3,7 @@
|
|||
"name": "AI Clock",
|
||||
"shortName":"AI Clock",
|
||||
"icon": "aiclock.png",
|
||||
"version":"0.04",
|
||||
"version":"0.05",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
||||
|
|
@ -11,7 +11,9 @@
|
|||
"tags": "clock",
|
||||
"screenshots": [
|
||||
{"url":"orig.png"},
|
||||
{"url":"impl.png"}
|
||||
{"url":"impl.png"},
|
||||
{"url":"impl_2.png"},
|
||||
{"url":"impl_3.png"}
|
||||
],
|
||||
"storage": [
|
||||
{"name":"aiclock.app.js","url":"aiclock.app.js"},
|
||||
|
|
|
|||
|
|
@ -17,3 +17,4 @@
|
|||
0.17: Now kick off Calendar sync as soon as connected to Gadgetbridge
|
||||
0.18: Use new message library
|
||||
If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged)
|
||||
0.19: Add automatic translation for a couple of strings.
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
t:event.cmd=="incoming"?"add":"remove",
|
||||
id:"call", src:"Phone",
|
||||
positive:true, negative:true,
|
||||
title:event.name||"Call", body:"Incoming call\n"+event.number});
|
||||
title:event.name||/*LANG*/"Call", body:/*LANG*/"Incoming call\n"+event.number});
|
||||
require("messages").pushMessage(event);
|
||||
},
|
||||
"alarm" : function() {
|
||||
|
|
@ -148,7 +148,7 @@
|
|||
Bangle.http = (url,options)=>{
|
||||
options = options||{};
|
||||
if (!NRF.getSecurityStatus().connected)
|
||||
return Promise.reject("Not connected to Bluetooth");
|
||||
return Promise.reject(/*LANG*/"Not connected to Bluetooth");
|
||||
if (Bangle.httpRequest === undefined)
|
||||
Bangle.httpRequest={};
|
||||
if (options.id === undefined) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"id": "android",
|
||||
"name": "Android Integration",
|
||||
"shortName": "Android",
|
||||
"version": "0.18",
|
||||
"version": "0.19",
|
||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||
|
|
|
|||
|
|
@ -61,3 +61,6 @@
|
|||
0.52: Ensure heading patch for pre-2v15.68 firmware applies to getCompass
|
||||
0.53: Add polyfills for pre-2v15.135 firmware for Bangle.load and Bangle.showClock
|
||||
0.54: Fix for invalid version comparison in polyfill
|
||||
0.55: Add toLocalISOString polyfill for pre-2v15 firmwares
|
||||
Only add boot info comments if settings.bootDebug was set
|
||||
If settings.bootDebug is set, output timing for each section of .boot0
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
/* This rewrites boot0.js based on current settings. If settings changed then it
|
||||
recalculates, but this avoids us doing a whole bunch of reconfiguration most
|
||||
of the time. */
|
||||
{ // execute in our own scope so we don't have to free variables...
|
||||
E.showMessage(/*LANG*/"Updating boot0...");
|
||||
var s = require('Storage').readJSON('setting.json',1)||{};
|
||||
var BANGLEJS2 = process.env.HWVERSION==2; // Is Bangle.js 2
|
||||
var FWVERSION = parseFloat(process.env.VERSION.replace("v","").replace(/\.(\d\d)$/,".0$1"));
|
||||
var boot = "", bootPost = "";
|
||||
let s = require('Storage').readJSON('setting.json',1)||{};
|
||||
const BANGLEJS2 = process.env.HWVERSION==2; // Is Bangle.js 2
|
||||
const FWVERSION = parseFloat(process.env.VERSION.replace("v","").replace(/\.(\d\d)$/,".0$1"));
|
||||
const DEBUG = s.bootDebug; // we can set this to enable debugging output in boot0
|
||||
let boot = "", bootPost = "";
|
||||
if (DEBUG) {
|
||||
boot += "var _tm=Date.now()\n";
|
||||
bootPost += "delete _tm;";
|
||||
}
|
||||
if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't changed
|
||||
var CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT);
|
||||
let CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT);
|
||||
boot += `if (E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
|
||||
} else {
|
||||
var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))+E.CRC32(process.env.GIT_COMMIT);
|
||||
let CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))+E.CRC32(process.env.GIT_COMMIT);
|
||||
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\\.boot\\.js/))+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
|
||||
}
|
||||
boot += ` { eval(require('Storage').read('bootupdate.js')); throw "Storage Updated!"}\n`;
|
||||
|
|
@ -88,14 +94,25 @@ delete Bangle.showClock;
|
|||
if (!Bangle.showClock) boot += `Bangle.showClock = ()=>{load(".bootcde")};\n`;
|
||||
delete Bangle.load;
|
||||
if (!Bangle.load) boot += `Bangle.load = load;\n`;
|
||||
let date = new Date();
|
||||
delete date.toLocalISOString; // toLocalISOString was only introduced in 2v15
|
||||
if (!date.toLocalISOString) boot += `Date.prototype.toLocalISOString = function() {
|
||||
var o = this.getTimezoneOffset();
|
||||
var d = new Date(this.getTime() - o*60000);
|
||||
var sign = o>0?"-":"+";
|
||||
o = Math.abs(o);
|
||||
return d.toISOString().slice(0,-1)+sign+Math.floor(o/60).toString().padStart(2,0)+(o%60).toString().padStart(2,0);
|
||||
};\n`;
|
||||
|
||||
// show timings
|
||||
if (DEBUG) boot += `print(".boot0",0|(Date.now()-_tm),"ms");_tm=Date.now();\n`
|
||||
// ================================================== BOOT.JS
|
||||
// Append *.boot.js files
|
||||
// These could change bleServices/bleServiceOptions if needed
|
||||
var bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{
|
||||
var getPriority = /.*\.(\d+)\.boot\.js$/;
|
||||
var aPriority = a.match(getPriority);
|
||||
var bPriority = b.match(getPriority);
|
||||
let bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{
|
||||
let getPriority = /.*\.(\d+)\.boot\.js$/;
|
||||
let aPriority = a.match(getPriority);
|
||||
let bPriority = b.match(getPriority);
|
||||
if (aPriority && bPriority){
|
||||
return parseInt(aPriority[1]) - parseInt(bPriority[1]);
|
||||
} else if (aPriority && !bPriority){
|
||||
|
|
@ -106,14 +123,16 @@ var bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{
|
|||
return a==b ? 0 : (a>b ? 1 : -1);
|
||||
});
|
||||
// precalculate file size
|
||||
var fileSize = boot.length + bootPost.length;
|
||||
let fileSize = boot.length + bootPost.length;
|
||||
bootFiles.forEach(bootFile=>{
|
||||
// match the size of data we're adding below in bootFiles.forEach
|
||||
fileSize += 2+bootFile.length+1+require('Storage').read(bootFile).length+2;
|
||||
if (DEBUG) fileSize += 2+bootFile.length+1; // `//${bootFile}\n` comment
|
||||
fileSize += require('Storage').read(bootFile).length+2; // boot code plus ";\n"
|
||||
if (DEBUG) fileSize += 48+E.toJS(bootFile).length; // `print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`
|
||||
});
|
||||
// write file in chunks (so as not to use up all RAM)
|
||||
require('Storage').write('.boot0',boot,0,fileSize);
|
||||
var fileOffset = boot.length;
|
||||
let fileOffset = boot.length;
|
||||
bootFiles.forEach(bootFile=>{
|
||||
// we add a semicolon so if the file is wrapped in (function(){ ... }()
|
||||
// with no semicolon we don't end up with (function(){ ... }()(function(){ ... }()
|
||||
|
|
@ -122,16 +141,18 @@ bootFiles.forEach(bootFile=>{
|
|||
// "//"+bootFile+"\n"+require('Storage').read(bootFile)+";\n";
|
||||
// but we need to do this without ever loading everything into RAM as some
|
||||
// boot files seem to be getting pretty big now.
|
||||
require('Storage').write('.boot0',"//"+bootFile+"\n",fileOffset);
|
||||
fileOffset+=2+bootFile.length+1;
|
||||
var bf = require('Storage').read(bootFile);
|
||||
if (DEBUG) {
|
||||
require('Storage').write('.boot0',`//${bootFile}\n`,fileOffset);
|
||||
fileOffset+=2+bootFile.length+1;
|
||||
}
|
||||
let bf = require('Storage').read(bootFile);
|
||||
// we can't just write 'bf' in one go because at least in 2v13 and earlier
|
||||
// Espruino wants to read the whole file into RAM first, and on Bangle.js 1
|
||||
// it can be too big (especially BTHRM).
|
||||
var bflen = bf.length;
|
||||
var bfoffset = 0;
|
||||
let bflen = bf.length;
|
||||
let bfoffset = 0;
|
||||
while (bflen) {
|
||||
var bfchunk = Math.min(bflen, 2048);
|
||||
let bfchunk = Math.min(bflen, 2048);
|
||||
require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset);
|
||||
fileOffset+=bfchunk;
|
||||
bfoffset+=bfchunk;
|
||||
|
|
@ -139,15 +160,14 @@ bootFiles.forEach(bootFile=>{
|
|||
}
|
||||
require('Storage').write('.boot0',";\n",fileOffset);
|
||||
fileOffset+=2;
|
||||
if (DEBUG) {
|
||||
require('Storage').write('.boot0',`print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`,fileOffset);
|
||||
fileOffset += 48+E.toJS(bootFile).length
|
||||
}
|
||||
});
|
||||
require('Storage').write('.boot0',bootPost,fileOffset);
|
||||
|
||||
delete boot;
|
||||
delete bootPost;
|
||||
delete bootFiles;
|
||||
delete fileSize;
|
||||
delete fileOffset;
|
||||
E.showMessage(/*LANG*/"Reloading...");
|
||||
eval(require('Storage').read('.boot0'));
|
||||
}
|
||||
// .bootcde should be run automatically after if required, since
|
||||
// we normally get called automatically from '.boot0'
|
||||
eval(require('Storage').read('.boot0'));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "boot",
|
||||
"name": "Bootloader",
|
||||
"version": "0.54",
|
||||
"version": "0.55",
|
||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||
"icon": "bootloader.png",
|
||||
"type": "bootloader",
|
||||
|
|
|
|||
|
|
@ -40,3 +40,4 @@
|
|||
0.16: Set powerdownRequested correctly on BTHRM power on
|
||||
Additional logging on errors
|
||||
Add debug option for disabling active scanning
|
||||
0.17: New GUI based on layout library
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
var intervalInt;
|
||||
var intervalBt;
|
||||
const BPM_FONT_SIZE="19%";
|
||||
const VALUE_TIMEOUT=3000;
|
||||
|
||||
var BODY_LOCS = {
|
||||
0: 'Other',
|
||||
|
|
@ -7,46 +7,119 @@ var BODY_LOCS = {
|
|||
2: 'Wrist',
|
||||
3: 'Finger',
|
||||
4: 'Hand',
|
||||
5: 'Ear Lobe',
|
||||
5: 'Earlobe',
|
||||
6: 'Foot',
|
||||
};
|
||||
|
||||
var Layout = require("Layout");
|
||||
|
||||
function border(l,c) {
|
||||
g.setColor(c).drawLine(l.x+l.w*0.05, l.y-4, l.x+l.w*0.95, l.y-4);
|
||||
}
|
||||
|
||||
function clear(y){
|
||||
g.reset();
|
||||
g.clearRect(0,y,g.getWidth(),y+75);
|
||||
}
|
||||
|
||||
function draw(y, type, event) {
|
||||
clear(y);
|
||||
var px = g.getWidth()/2;
|
||||
var str = event.bpm + "";
|
||||
g.reset();
|
||||
g.setFontAlign(0,0);
|
||||
g.setFontVector(40).drawString(str,px,y+20);
|
||||
str = "Event: " + type;
|
||||
if (type === "HRM") {
|
||||
str += " Confidence: " + event.confidence;
|
||||
g.setFontVector(12).drawString(str,px,y+40);
|
||||
str = " Source: " + (event.src ? event.src : "internal");
|
||||
g.setFontVector(12).drawString(str,px,y+50);
|
||||
function getRow(id, text, additionalInfo){
|
||||
let additional = [];
|
||||
let l = {
|
||||
type:"h", c: [
|
||||
{
|
||||
type:"v",
|
||||
width: g.getWidth()*0.4,
|
||||
c: [
|
||||
{type:"txt", halign:1, font:"8%", label:text, id:id+"text" },
|
||||
{type:"txt", halign:1, font:BPM_FONT_SIZE, label:"--", id:id, bgCol: g.theme.bg }
|
||||
]
|
||||
},{
|
||||
type:undefined, fillx:1
|
||||
},{
|
||||
type:"v",
|
||||
valign: -1,
|
||||
width: g.getWidth()*0.45,
|
||||
c: additional
|
||||
},{
|
||||
type:undefined, width:g.getWidth()*0.05
|
||||
}
|
||||
]
|
||||
};
|
||||
for (let i of additionalInfo){
|
||||
let label = {type:"txt", font:"6x8", label:i + ":" };
|
||||
let value = {type:"txt", font:"6x8", label:"--", id:id + i };
|
||||
additional.push({type:"h", halign:-1, c:[ label, {type:undefined, fillx:1}, value ]});
|
||||
}
|
||||
if (type === "BTHRM"){
|
||||
if (event.battery) str += " Bat: " + (event.battery ? event.battery : "");
|
||||
g.setFontVector(12).drawString(str,px,y+40);
|
||||
str= "";
|
||||
if (event.location) str += "Loc: " + BODY_LOCS[event.location];
|
||||
if (event.rr && event.rr.length > 0) str += " RR: " + event.rr.join(",");
|
||||
g.setFontVector(12).drawString(str,px,y+50);
|
||||
str= "";
|
||||
if (event.contact) str += " Contact: " + event.contact;
|
||||
if (event.energy) str += " kJoule: " + event.energy.toFixed(0);
|
||||
g.setFontVector(12).drawString(str,px,y+60);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
var layout = new Layout( {
|
||||
type:"v", c: [
|
||||
getRow("int", "INT", ["Confidence"]),
|
||||
getRow("agg", "HRM", ["Confidence", "Source"]),
|
||||
getRow("bt", "BT", ["Battery","Location","Contact", "RR", "Energy"]),
|
||||
{ type:undefined, height:8 } //dummy to protect debug output
|
||||
]
|
||||
}, {
|
||||
lazy:true
|
||||
});
|
||||
|
||||
var int,agg,bt;
|
||||
var firstEvent = true;
|
||||
|
||||
function draw(){
|
||||
if (!(int || agg || bt)) return;
|
||||
|
||||
if (firstEvent) {
|
||||
g.clearRect(Bangle.appRect);
|
||||
firstEvent = false;
|
||||
}
|
||||
|
||||
let now = Date.now();
|
||||
|
||||
if (int && int.time > (now - VALUE_TIMEOUT)){
|
||||
layout.int.label = int.bpm;
|
||||
if (!isNaN(int.confidence)) layout.intConfidence.label = int.confidence;
|
||||
} else {
|
||||
layout.int.label = "--";
|
||||
layout.intConfidence.label = "--";
|
||||
}
|
||||
|
||||
if (agg && agg.time > (now - VALUE_TIMEOUT)){
|
||||
layout.agg.label = agg.bpm;
|
||||
if (!isNaN(agg.confidence)) layout.aggConfidence.label = agg.confidence;
|
||||
if (agg.src) layout.aggSource.label = agg.src;
|
||||
} else {
|
||||
layout.agg.label = "--";
|
||||
layout.aggConfidence.label = "--";
|
||||
layout.aggSource.label = "--";
|
||||
}
|
||||
|
||||
if (bt && bt.time > (now - VALUE_TIMEOUT)) {
|
||||
layout.bt.label = bt.bpm;
|
||||
if (!isNaN(bt.battery)) layout.btBattery.label = bt.battery + "%";
|
||||
if (bt.rr) layout.btRR.label = bt.rr.join(",");
|
||||
if (!isNaN(bt.location)) layout.btLocation.label = BODY_LOCS[bt.location];
|
||||
if (bt.contact !== undefined) layout.btContact.label = bt.contact ? "Yes":"No";
|
||||
if (!isNaN(bt.energy)) layout.btEnergy.label = bt.energy.toFixed(0) + "kJ";
|
||||
} else {
|
||||
layout.bt.label = "--";
|
||||
layout.btBattery.label = "--";
|
||||
layout.btRR.label = "--";
|
||||
layout.btLocation.label = "--";
|
||||
layout.btContact.label = "--";
|
||||
layout.btEnergy.label = "--";
|
||||
}
|
||||
|
||||
layout.update();
|
||||
layout.render();
|
||||
let first = true;
|
||||
for (let c of layout.l.c){
|
||||
if (first) {
|
||||
first = false;
|
||||
continue;
|
||||
}
|
||||
if (c.type && c.type == "h")
|
||||
border(c,g.theme.fg);
|
||||
}
|
||||
}
|
||||
|
||||
var firstEventBt = true;
|
||||
var firstEventInt = true;
|
||||
|
||||
|
||||
// This can get called for the boot code to show what's happening
|
||||
function showStatusInfo(txt) {
|
||||
|
|
@ -57,41 +130,26 @@ function showStatusInfo(txt) {
|
|||
}
|
||||
|
||||
function onBtHrm(e) {
|
||||
if (firstEventBt){
|
||||
clear(24);
|
||||
firstEventBt = false;
|
||||
}
|
||||
draw(100, "BTHRM", e);
|
||||
if (e.bpm === 0){
|
||||
Bangle.buzz(100,0.2);
|
||||
}
|
||||
if (intervalBt){
|
||||
clearInterval(intervalBt);
|
||||
}
|
||||
intervalBt = setInterval(()=>{
|
||||
clear(100);
|
||||
}, 2000);
|
||||
bt = e;
|
||||
bt.time = Date.now();
|
||||
}
|
||||
|
||||
function onHrm(e) {
|
||||
if (firstEventInt){
|
||||
clear(24);
|
||||
firstEventInt = false;
|
||||
}
|
||||
draw(24, "HRM", e);
|
||||
if (intervalInt){
|
||||
clearInterval(intervalInt);
|
||||
}
|
||||
intervalInt = setInterval(()=>{
|
||||
clear(24);
|
||||
}, 2000);
|
||||
function onInt(e) {
|
||||
int = e;
|
||||
int.time = Date.now();
|
||||
}
|
||||
|
||||
function onAgg(e) {
|
||||
agg = e;
|
||||
agg.time = Date.now();
|
||||
}
|
||||
|
||||
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
||||
|
||||
Bangle.on('BTHRM', onBtHrm);
|
||||
Bangle.on('HRM', onHrm);
|
||||
Bangle.on('HRM_int', onInt);
|
||||
Bangle.on('HRM', onAgg);
|
||||
|
||||
|
||||
Bangle.setHRMPower(1,'bthrm');
|
||||
if (!(settings.startWithHrm)){
|
||||
|
|
@ -103,10 +161,11 @@ Bangle.loadWidgets();
|
|||
Bangle.drawWidgets();
|
||||
if (Bangle.setBTHRMPower){
|
||||
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 24);
|
||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2);
|
||||
setInterval(draw, 1000);
|
||||
} else {
|
||||
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
||||
g.drawString("BTHRM disabled",g.getWidth()/2,g.getHeight()/2 + 32);
|
||||
g.drawString("BTHRM disabled",g.getWidth()/2,g.getHeight()/2);
|
||||
}
|
||||
|
||||
E.on('kill', ()=>Bangle.setBTHRMPower(0,'bthrm'));
|
||||
|
|
|
|||
|
|
@ -553,14 +553,15 @@ exports.enable = () => {
|
|||
|
||||
if (settings.replace){
|
||||
// register a listener for original HRM events and emit as HRM_int
|
||||
Bangle.on("HRM", (e) => {
|
||||
e.modified = true;
|
||||
Bangle.on("HRM", (o) => {
|
||||
let e = Object.assign({},o);
|
||||
log("Emitting HRM_int", e);
|
||||
Bangle.emit("HRM_int", e);
|
||||
if (fallbackActive){
|
||||
// if fallback to internal HRM is active, emit as HRM_R to which everyone listens
|
||||
log("Emitting HRM_R(int)", e);
|
||||
Bangle.emit("HRM_R", e);
|
||||
o.src = "int";
|
||||
log("Emitting HRM_R(int)", o);
|
||||
Bangle.emit("HRM_R", o);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -576,6 +577,13 @@ exports.enable = () => {
|
|||
if (name == "HRM") o("HRM_R", cb);
|
||||
else o(name, cb);
|
||||
})(Bangle.removeListener);
|
||||
} else {
|
||||
Bangle.on("HRM", (o)=>{
|
||||
o.src = "int";
|
||||
let e = Object.assign({},o);
|
||||
log("Emitting HRM_int", e);
|
||||
Bangle.emit("HRM_int", e);
|
||||
});
|
||||
}
|
||||
|
||||
Bangle.origSetHRMPower = Bangle.setHRMPower;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
"id": "bthrm",
|
||||
"name": "Bluetooth Heart Rate Monitor",
|
||||
"shortName": "BT HRM",
|
||||
"version": "0.16",
|
||||
"version": "0.17",
|
||||
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screen.png"}],
|
||||
"type": "app",
|
||||
"tags": "health,bluetooth,hrm,bthrm",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -7,3 +7,4 @@
|
|||
0.25: Fixed a bug that would let widgets change the color of the clock.
|
||||
0.26: Time formatted to locale
|
||||
0.27: Fixed the timing code, which sometimes did not update for one minute
|
||||
0.28: More config options for cleaner look, enabled fast loading
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
# New Features:
|
||||
- Fast load! (only works if your launcher uses widgets)
|
||||
- widgets, date and weekday are individually configurable
|
||||
- you can hide widgets, date and weekday for a cleaner look when the watch is locked
|
||||
|
||||
Contact me for bug reports or feature requests: ContourClock@gmx.de
|
||||
|
|
@ -1,35 +1,64 @@
|
|||
var digits = [];
|
||||
var drawTimeout;
|
||||
var fontName="";
|
||||
var settings = require('Storage').readJSON("contourclock.json", true) || {};
|
||||
if (settings.fontIndex==undefined) {
|
||||
settings.fontIndex=0;
|
||||
require('Storage').writeJSON("myapp.json", settings);
|
||||
}
|
||||
{
|
||||
let digits = [];
|
||||
let drawTimeout;
|
||||
let fontName="";
|
||||
let settings = require('Storage').readJSON("contourclock.json", true) || {};
|
||||
if (settings.fontIndex==undefined) {
|
||||
settings.fontIndex=0;
|
||||
settings.widgets=true;
|
||||
settings.hide=false;
|
||||
settings.weekday=true;
|
||||
settings.hideWhenLocked=false;
|
||||
settings.date=true; require('Storage').writeJSON("myapp.json", settings);
|
||||
}
|
||||
|
||||
function queueDraw() {
|
||||
setTimeout(function() {
|
||||
let queueDraw = function() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
queueDraw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
};
|
||||
|
||||
let draw = function() {
|
||||
var date = new Date();
|
||||
// Draw day of the week
|
||||
g.reset();
|
||||
if ((!settings.hideWhenLocked) || (!Bangle.isLocked())) {
|
||||
// Draw day of the week
|
||||
g.setFont("Teletext10x18Ascii");
|
||||
g.clearRect(0,138,g.getWidth()-1,176);
|
||||
if (settings.weekday) g.setFontAlign(0,1).drawString(require("locale").dow(date).toUpperCase(),g.getWidth()/2,g.getHeight()-18);
|
||||
// Draw Date
|
||||
if (settings.date) g.setFontAlign(0,1).drawString(require('locale').date(new Date(),1),g.getWidth()/2,g.getHeight());
|
||||
}
|
||||
require('contourclock').drawClock(settings.fontIndex);
|
||||
};
|
||||
|
||||
require("FontTeletext10x18Ascii").add(Graphics);
|
||||
g.clear();
|
||||
|
||||
draw();
|
||||
if (settings.hideWhenLocked) Bangle.on('lock', function (locked) {
|
||||
if (!locked) require("widget_utils").show();
|
||||
else {
|
||||
g.clear();
|
||||
if (settings.hide) require("widget_utils").swipeOn();
|
||||
else require("widget_utils").hide();
|
||||
}
|
||||
draw();
|
||||
queueDraw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
});
|
||||
Bangle.setUI({mode:"clock", remove:function() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
if (settings.widgets && settings.hide) require("widget_utils").show();
|
||||
g.reset();
|
||||
g.clear();
|
||||
}});
|
||||
if (settings.widgets) {
|
||||
Bangle.loadWidgets();
|
||||
if (settings.hide) require("widget_utils").swipeOn();
|
||||
else Bangle.drawWidgets();
|
||||
}
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var date = new Date();
|
||||
// Draw day of the week
|
||||
g.reset();
|
||||
g.setFont("Teletext10x18Ascii");
|
||||
g.clearRect(0,138,g.getWidth()-1,176);
|
||||
g.setFontAlign(0,1).drawString(require("locale").dow(date).toUpperCase(),g.getWidth()/2,g.getHeight()-18);
|
||||
// Draw Date
|
||||
g.setFontAlign(0,1).drawString(require('locale').date(new Date(),1),g.getWidth()/2,g.getHeight());
|
||||
require('contourclock').drawClock(settings.fontIndex);
|
||||
}
|
||||
|
||||
require("FontTeletext10x18Ascii").add(Graphics);
|
||||
Bangle.setUI("clock");
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
queueDraw();
|
||||
draw();
|
||||
|
|
|
|||
|
|
@ -1,43 +1,73 @@
|
|||
(function(back) {
|
||||
Bangle.removeAllListeners('drag');
|
||||
Bangle.setUI("");
|
||||
var settings = require('Storage').readJSON('contourclock.json', true) || {};
|
||||
if (settings.fontIndex==undefined) {
|
||||
settings.fontIndex=0;
|
||||
settings.fontIndex=0;
|
||||
settings.widgets=true;
|
||||
settings.hide=false;
|
||||
settings.weekday=true;
|
||||
settings.date=true;
|
||||
settings.hideWhenLocked=false;
|
||||
require('Storage').writeJSON("myapp.json", settings);
|
||||
}
|
||||
savedIndex=settings.fontIndex;
|
||||
saveListener = setWatch(function() { //save changes and return to settings menu
|
||||
require('Storage').writeJSON('contourclock.json', settings);
|
||||
Bangle.removeAllListeners('swipe');
|
||||
Bangle.removeAllListeners('lock');
|
||||
clearWatch(saveListener);
|
||||
g.clear();
|
||||
back();
|
||||
}, BTN, { repeat:false, edge:'falling' });
|
||||
lockListener = Bangle.on('lock', function () { //discard changes and return to clock
|
||||
settings.fontIndex=savedIndex;
|
||||
require('Storage').writeJSON('contourclock.json', settings);
|
||||
Bangle.removeAllListeners('swipe');
|
||||
Bangle.removeAllListeners('lock');
|
||||
clearWatch(saveListener);
|
||||
g.clear();
|
||||
load();
|
||||
});
|
||||
swipeListener = Bangle.on('swipe', function (direction) {
|
||||
var fontName = require('contourclock').drawClock(settings.fontIndex+direction);
|
||||
if (fontName) {
|
||||
settings.fontIndex+=direction;
|
||||
g.clearRect(0,0,g.getWidth()-1,16);
|
||||
g.setFont('6x8:2x2').setFontAlign(0,-1).drawString(fontName,g.getWidth()/2,0);
|
||||
} else {
|
||||
require('contourclock').drawClock(settings.fontIndex);
|
||||
}
|
||||
});
|
||||
g.reset();
|
||||
g.clear();
|
||||
g.setFont('6x8:2x2').setFontAlign(0,-1);
|
||||
g.drawString(require('contourclock').drawClock(settings.fontIndex),g.getWidth()/2,0);
|
||||
g.drawString('Swipe - change',g.getWidth()/2,g.getHeight()-36);
|
||||
g.drawString('BTN - save',g.getWidth()/2,g.getHeight()-18);
|
||||
function mainMenu() {
|
||||
E.showMenu({
|
||||
"" : { "title" : "ContourClock" },
|
||||
"< Back" : () => back(),
|
||||
'Widgets': {
|
||||
value: (settings.widgets !== undefined ? settings.widgets : true),
|
||||
onchange : v => {settings.widgets=v; require('Storage').writeJSON('contourclock.json', settings);}
|
||||
},
|
||||
'hide Widgets': {
|
||||
value: (settings.hide !== undefined ? settings.hide : false),
|
||||
onchange : v => {settings.hide=v; require('Storage').writeJSON('contourclock.json', settings);}
|
||||
},
|
||||
'Weekday': {
|
||||
value: (settings.weekday !== undefined ? settings.weekday : true),
|
||||
onchange : v => {settings.weekday=v; require('Storage').writeJSON('contourclock.json', settings);}
|
||||
},
|
||||
'Date': {
|
||||
value: (settings.date !== undefined ? settings.date : true),
|
||||
onchange : v => {settings.date=v; require('Storage').writeJSON('contourclock.json', settings);}
|
||||
},
|
||||
'Hide when locked': {
|
||||
value: (settings.hideWhenLocked !== undefined ? settings.hideWhenLocked : false),
|
||||
onchange : v => {settings.hideWhenLocked=v; require('Storage').writeJSON('contourclock.json', settings);}
|
||||
},
|
||||
'set Font': () => fontMenu()
|
||||
});
|
||||
}
|
||||
function fontMenu() {
|
||||
Bangle.setUI("");
|
||||
savedIndex=settings.fontIndex;
|
||||
saveListener = setWatch(function() { //save changes and return to settings menu
|
||||
require('Storage').writeJSON('contourclock.json', settings);
|
||||
Bangle.removeAllListeners('swipe');
|
||||
Bangle.removeAllListeners('lock');
|
||||
mainMenu();
|
||||
}, BTN, { repeat:false, edge:'falling' });
|
||||
lockListener = Bangle.on('lock', function () { //discard changes and return to clock
|
||||
settings.fontIndex=savedIndex;
|
||||
require('Storage').writeJSON('contourclock.json', settings);
|
||||
Bangle.removeAllListeners('swipe');
|
||||
Bangle.removeAllListeners('lock');
|
||||
mainMenu();
|
||||
});
|
||||
swipeListener = Bangle.on('swipe', function (direction) {
|
||||
var fontName = require('contourclock').drawClock(settings.fontIndex+direction);
|
||||
if (fontName) {
|
||||
settings.fontIndex+=direction;
|
||||
g.clearRect(0,g.getHeight()-36,g.getWidth()-1,g.getHeight()-36+16);
|
||||
g.setFont('6x8:2x2').setFontAlign(0,-1).drawString(fontName,g.getWidth()/2,g.getHeight()-36);
|
||||
} else {
|
||||
require('contourclock').drawClock(settings.fontIndex);
|
||||
}
|
||||
});
|
||||
g.reset();
|
||||
g.clearRect(0,24,g.getWidth()-1,g.getHeight()-1);
|
||||
g.setFont('6x8:2x2').setFontAlign(0,-1);
|
||||
g.drawString(require('contourclock').drawClock(settings.fontIndex),g.getWidth()/2,g.getHeight()-36);
|
||||
g.drawString('Button to save',g.getWidth()/2,g.getHeight()-18);
|
||||
}
|
||||
mainMenu();
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
{ "id": "contourclock",
|
||||
"name": "Contour Clock",
|
||||
"shortName" : "Contour Clock",
|
||||
"version":"0.27",
|
||||
"version":"0.28",
|
||||
"icon": "app.png",
|
||||
"description": "A Minimalist clockface with large Digits. Now with more fonts!",
|
||||
"readme": "README.md",
|
||||
"description": "A Minimalist clockface with large Digits.",
|
||||
"screenshots" : [{"url":"cc-screenshot-1.png"},{"url":"cc-screenshot-2.png"}],
|
||||
"tags": "clock",
|
||||
"custom": "custom.html",
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@
|
|||
0.05: Tweaks to help with memory usage
|
||||
0.06: Reduce memory usage
|
||||
0.07: Allow negative numbers when manual-sorting
|
||||
0.08: Automatic translation of strings.
|
||||
|
|
|
|||
|
|
@ -3,20 +3,20 @@ const store = require('Storage');
|
|||
function showMainMenu() {
|
||||
const mainmenu = {
|
||||
'': {
|
||||
'title': 'App Manager',
|
||||
'title': /*LANG*/'App Manager',
|
||||
},
|
||||
'< Back': ()=> {load();},
|
||||
'Sort Apps': () => showSortAppsMenu(),
|
||||
'Manage Apps': ()=> showApps(),
|
||||
'Compact': () => {
|
||||
E.showMessage('Compacting...');
|
||||
/*LANG*/'Sort Apps': () => showSortAppsMenu(),
|
||||
/*LANG*/'Manage Apps': ()=> showApps(),
|
||||
/*LANG*/'Compact': () => {
|
||||
E.showMessage(/*LANG*/'Compacting...');
|
||||
try {
|
||||
store.compact();
|
||||
} catch (e) {
|
||||
}
|
||||
showMainMenu();
|
||||
},
|
||||
'Free': {
|
||||
/*LANG*/'Free': {
|
||||
value: undefined,
|
||||
format: (v) => {
|
||||
return store.getFree();
|
||||
|
|
@ -65,13 +65,13 @@ function eraseData(info) {
|
|||
});
|
||||
}
|
||||
function eraseApp(app, files,data) {
|
||||
E.showMessage('Erasing\n' + app.name + '...');
|
||||
E.showMessage(/*LANG*/'Erasing\n' + app.name + '...');
|
||||
var info = store.readJSON(app.id + ".info", 1)||{};
|
||||
if (files) eraseFiles(info);
|
||||
if (data) eraseData(info);
|
||||
}
|
||||
function eraseOne(app, files,data){
|
||||
E.showPrompt('Erase\n'+app.name+'?').then((v) => {
|
||||
E.showPrompt(/*LANG*/'Erase\n'+app.name+'?').then((v) => {
|
||||
if (v) {
|
||||
Bangle.buzz(100, 1);
|
||||
eraseApp(app, files, data);
|
||||
|
|
@ -82,7 +82,7 @@ function eraseOne(app, files,data){
|
|||
});
|
||||
}
|
||||
function eraseAll(apps, files,data) {
|
||||
E.showPrompt('Erase all?').then((v) => {
|
||||
E.showPrompt(/*LANG*/'Erase all?').then((v) => {
|
||||
if (v) {
|
||||
Bangle.buzz(100, 1);
|
||||
apps.forEach(app => eraseApp(app, files, data));
|
||||
|
|
@ -99,11 +99,11 @@ function showAppMenu(app) {
|
|||
'< Back': () => showApps(),
|
||||
};
|
||||
if (app.hasData) {
|
||||
appmenu['Erase Completely'] = () => eraseOne(app, true, true);
|
||||
appmenu['Erase App,Keep Data'] = () => eraseOne(app, true, false);
|
||||
appmenu['Only Erase Data'] = () => eraseOne(app, false, true);
|
||||
appmenu[/*LANG*/'Erase Completely'] = () => eraseOne(app, true, true);
|
||||
appmenu[/*LANG*/'Erase App,Keep Data'] = () => eraseOne(app, true, false);
|
||||
appmenu[/*LANG*/'Only Erase Data'] = () => eraseOne(app, false, true);
|
||||
} else {
|
||||
appmenu['Erase'] = () => eraseOne(app, true, false);
|
||||
appmenu[/*LANG*/'Erase'] = () => eraseOne(app, true, false);
|
||||
}
|
||||
E.showMenu(appmenu);
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ function showAppMenu(app) {
|
|||
function showApps() {
|
||||
const appsmenu = {
|
||||
'': {
|
||||
'title': 'Apps',
|
||||
'title': /*LANG*/'Apps',
|
||||
},
|
||||
'< Back': () => showMainMenu(),
|
||||
};
|
||||
|
|
@ -128,17 +128,17 @@ function showApps() {
|
|||
menu[app.name] = () => showAppMenu(app);
|
||||
return menu;
|
||||
}, appsmenu);
|
||||
appsmenu['Erase All'] = () => {
|
||||
appsmenu[/*LANG*/'Erase All'] = () => {
|
||||
E.showMenu({
|
||||
'': {'title': 'Erase All'},
|
||||
'Erase Everything': () => eraseAll(list, true, true),
|
||||
'Erase Apps,Keep Data': () => eraseAll(list, true, false),
|
||||
'Only Erase Data': () => eraseAll(list, false, true),
|
||||
'': {'title': /*LANG*/'Erase All'},
|
||||
/*LANG*/'Erase Everything': () => eraseAll(list, true, true),
|
||||
/*LANG*/'Erase Apps,Keep Data': () => eraseAll(list, true, false),
|
||||
/*LANG*/'Only Erase Data': () => eraseAll(list, false, true),
|
||||
'< Back': () => showApps(),
|
||||
});
|
||||
};
|
||||
} else {
|
||||
appsmenu['...No Apps...'] = {
|
||||
appsmenu[/*LANG*/'...No Apps...'] = {
|
||||
value: undefined,
|
||||
format: ()=> '',
|
||||
onchange: ()=> {}
|
||||
|
|
@ -150,16 +150,16 @@ function showApps() {
|
|||
function showSortAppsMenu() {
|
||||
const sorterMenu = {
|
||||
'': {
|
||||
'title': 'App Sorter',
|
||||
'title': /*LANG*/'App Sorter',
|
||||
},
|
||||
'< Back': () => showMainMenu(),
|
||||
'Sort: manually': ()=> showSortAppsManually(),
|
||||
'Sort: alph. ASC': () => {
|
||||
E.showMessage('Sorting:\nAlphabetically\nascending ...');
|
||||
/*LANG*/'Sort: manually': ()=> showSortAppsManually(),
|
||||
/*LANG*/'Sort: alph. ASC': () => {
|
||||
E.showMessage(/*LANG*/'Sorting:\nAlphabetically\nascending ...');
|
||||
sortAlphabet(false);
|
||||
},
|
||||
'Sort: alph. DESC': () => {
|
||||
E.showMessage('Sorting:\nAlphabetically\ndescending ...');
|
||||
E.showMessage(/*LANG*/'Sorting:\nAlphabetically\ndescending ...');
|
||||
sortAlphabet(true);
|
||||
}
|
||||
};
|
||||
|
|
@ -169,7 +169,7 @@ function showSortAppsMenu() {
|
|||
function showSortAppsManually() {
|
||||
const appsSorterMenu = {
|
||||
'': {
|
||||
'title': 'Sort: manually',
|
||||
'title': /*LANG*/'Sort: manually',
|
||||
},
|
||||
'< Back': () => showSortAppsMenu(),
|
||||
};
|
||||
|
|
@ -186,7 +186,7 @@ function showSortAppsManually() {
|
|||
return menu;
|
||||
}, appsSorterMenu);
|
||||
} else {
|
||||
appsSorterMenu['...No Apps...'] = {
|
||||
appsSorterMenu[/*LANG*/'...No Apps...'] = {
|
||||
value: undefined,
|
||||
format: ()=> '',
|
||||
onchange: ()=> {}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "files",
|
||||
"name": "App Manager",
|
||||
"version": "0.07",
|
||||
"version": "0.08",
|
||||
"description": "Show currently installed apps, free space, and allow their deletion from the watch",
|
||||
"icon": "files.png",
|
||||
"tags": "tool,system,files",
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@
|
|||
Reconstruct battery voltage by using calibrated batFullVoltage
|
||||
Averaging for smoothing compass headings
|
||||
Save state if route or waypoint has been chosen
|
||||
0.09: Workaround a minifier issue allowing to install gpstrek with minification enabled
|
||||
|
|
|
|||
|
|
@ -259,7 +259,8 @@ let getCompassSlice = function(compassDataSource){
|
|||
|
||||
|
||||
if (compassDataSource.getPoints){
|
||||
for (let p of compassDataSource.getPoints()){
|
||||
let points = compassDataSource.getPoints(); //storing this in a variable works around a minifier bug causing a problem in the next line: for(let a of a.getPoints())
|
||||
for (let p of points){
|
||||
g.reset();
|
||||
var bpos = p.bearing - lastDrawnValue;
|
||||
if (bpos>180) bpos -=360;
|
||||
|
|
@ -285,7 +286,8 @@ let getCompassSlice = function(compassDataSource){
|
|||
}
|
||||
}
|
||||
if (compassDataSource.getMarkers){
|
||||
for (let m of compassDataSource.getMarkers()){
|
||||
let markers = compassDataSource.getMarkers(); //storing this in a variable works around a minifier bug causing a problem in the next line: for(let a of a.getMarkers())
|
||||
for (let m of markers){
|
||||
g.reset();
|
||||
g.setColor(m.fillcolor);
|
||||
let mpos = m.xpos * width;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gpstrek",
|
||||
"name": "GPS Trekking",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}],
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Added clkinfo for clocks.
|
||||
0.04: Feedback if clkinfo run is called.
|
||||
0.05: Clkinfo improvements.
|
||||
0.06: Updated clkinfo icon.
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
var haItems = {
|
||||
name: "Home",
|
||||
img: atob("GBiBAf/////////n///D//+B//8A//48T/wkD/gkD/A8D+AYB8AYA4eZ4QyZMOyZN+fb5+D/B+B+B+A8B+AYB+AYB+AYB+AYB+A8Bw=="),
|
||||
img: atob("GBiBAAAAAAAAAAAAAAAYAAA+AAB+AADD4AHb4APD4Afn8A/n+BxmOD0mnA0ksAwAMA+B8A/D8A/n8A/n8A/n8A/n8AAAAAAAAAAAAA=="),
|
||||
items: []
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "ha",
|
||||
"name": "HomeAssistant",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "Integrates your BangleJS into HomeAssistant.",
|
||||
"icon": "ha.png",
|
||||
"type": "app",
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@
|
|||
0.08: Don't force backlight on/watch unlocked on Bangle 2
|
||||
0.09: Grey out BPM until confidence is over 50%
|
||||
0.10: Autoscale raw graph to maximum value seen
|
||||
0.11: Automatic translation of strings.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ function updateHrm(){
|
|||
var px = g.getWidth()/2;
|
||||
g.setFontAlign(0,-1);
|
||||
g.clearRect(0,24,g.getWidth(),80);
|
||||
g.setFont("6x8").drawString("Confidence "+(hrmInfo.confidence || "--")+"%", px, 70);
|
||||
g.setFont("6x8").drawString(/*LANG*/"Confidence "+(hrmInfo.confidence || "--")+"%", px, 70);
|
||||
|
||||
updateScale();
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ function updateHrm(){
|
|||
g.setFontVector(40).setColor(hrmInfo.confidence > 50 ? g.theme.fg : "#888").drawString(str,px,45);
|
||||
px += g.stringWidth(str)/2;
|
||||
g.setFont("6x8").setColor(g.theme.fg);
|
||||
g.drawString("BPM",px+15,45);
|
||||
g.drawString(/*LANG*/"BPM",px+15,45);
|
||||
}
|
||||
|
||||
function updateScale(){
|
||||
|
|
@ -101,7 +101,7 @@ Bangle.loadWidgets();
|
|||
Bangle.drawWidgets();
|
||||
g.setColor(g.theme.fg);
|
||||
g.reset().setFont("6x8",2).setFontAlign(0,-1);
|
||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
||||
g.drawString(/*LANG*/"Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
||||
countDown();
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "hrm",
|
||||
"name": "Heart Rate Monitor",
|
||||
"version": "0.10",
|
||||
"version": "0.11",
|
||||
"description": "Measure your heart rate and see live sensor data",
|
||||
"icon": "heartrate.png",
|
||||
"tags": "health",
|
||||
|
|
|
|||
|
|
@ -627,7 +627,7 @@ s.pl = {};
|
|||
Bangle.drawWidgets = ()=>{};
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets = orig;
|
||||
require("widget_utils").swipeOn();
|
||||
require("widget_utils").swipeOn(0);
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
}).catch((e)=>{
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Positioning of marker now takes the height of the widget field into account.
|
||||
0.04: Fix issue if going back without typing.
|
||||
0.05: Keep drag-function in ram, hopefully improving performance and input reliability somewhat.
|
||||
0.06: Support input of numbers and uppercase characters.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ A library that provides the ability to input text by swiping PalmOS Graffiti-sty
|
|||
|
||||
To get a legend of available characters, just tap the screen.
|
||||
|
||||
To switch between the input of alphabetic and numeric characters tap the widget which displays either "123" or "ABC".
|
||||
|
||||
To switch between lowercase and uppercase characters do an up swipe.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
|
|
|||
|
|
@ -1,47 +1,68 @@
|
|||
exports.INPUT_MODE_ALPHA = 0;
|
||||
exports.INPUT_MODE_NUM = 1;
|
||||
|
||||
/* To make your own strokes, type:
|
||||
|
||||
Bangle.on('stroke',print)
|
||||
|
||||
on the left of the IDE, then do a stroke and copy out the Uint8Array line
|
||||
*/
|
||||
exports.getStrokes = function(cb) {
|
||||
cb("a", new Uint8Array([58, 159, 58, 155, 62, 144, 69, 127, 77, 106, 86, 90, 94, 77, 101, 68, 108, 62, 114, 59, 121, 59, 133, 61, 146, 70, 158, 88, 169, 107, 176, 124, 180, 135, 183, 144, 185, 152]));
|
||||
cb("b", new Uint8Array([51, 47, 51, 77, 56, 123, 60, 151, 65, 163, 68, 164, 68, 144, 67, 108, 67, 76, 72, 43, 104, 51, 121, 74, 110, 87, 109, 95, 131, 117, 131, 140, 109, 152, 88, 157]));
|
||||
cb("c", new Uint8Array([153, 62, 150, 62, 145, 62, 136, 62, 123, 62, 106, 65, 85, 70, 65, 75, 50, 82, 42, 93, 37, 106, 36, 119, 36, 130, 40, 140, 49, 147, 61, 153, 72, 156, 85, 157, 106, 158, 116, 158]));
|
||||
cb("d", new Uint8Array([57, 178, 57, 176, 55, 171, 52, 163, 50, 154, 49, 146, 47, 135, 45, 121, 44, 108, 44, 97, 44, 85, 44, 75, 44, 66, 44, 58, 44, 48, 44, 38, 46, 31, 48, 26, 58, 21, 75, 20, 99, 26, 120, 35, 136, 51, 144, 70, 144, 88, 137, 110, 124, 131, 106, 145, 88, 153]));
|
||||
cb("e", new Uint8Array([150, 72, 141, 69, 114, 68, 79, 69, 48, 77, 32, 81, 31, 85, 46, 91, 73, 95, 107, 100, 114, 103, 83, 117, 58, 134, 66, 143, 105, 148, 133, 148, 144, 148]));
|
||||
cb("f", new Uint8Array([157, 52, 155, 52, 148, 52, 137, 52, 124, 52, 110, 52, 96, 52, 83, 52, 74, 52, 67, 52, 61, 52, 57, 52, 55, 52, 52, 52, 52, 54, 52, 58, 52, 64, 54, 75, 58, 97, 59, 117, 60, 130]));
|
||||
cb("g", new Uint8Array([160, 66, 153, 62, 129, 58, 90, 56, 58, 57, 38, 65, 31, 86, 43, 125, 69, 152, 116, 166, 145, 154, 146, 134, 112, 116, 85, 108, 97, 106, 140, 106, 164, 106]));
|
||||
cb("h", new Uint8Array([58, 50, 58, 55, 58, 64, 58, 80, 58, 102, 58, 122, 58, 139, 58, 153, 58, 164, 58, 171, 58, 177, 58, 179, 58, 181, 58, 180, 58, 173, 58, 163, 59, 154, 61, 138, 64, 114, 68, 95, 72, 84, 80, 79, 91, 79, 107, 82, 123, 93, 137, 111, 145, 130, 149, 147, 150, 154, 150, 159]));
|
||||
cb("i", new Uint8Array([89, 48, 89, 49, 89, 51, 89, 55, 89, 60, 89, 68, 89, 78, 89, 91, 89, 103, 89, 114, 89, 124, 89, 132, 89, 138, 89, 144, 89, 148, 89, 151, 89, 154, 89, 156, 89, 157, 89, 158]));
|
||||
cb("j", new Uint8Array([130, 57, 130, 61, 130, 73, 130, 91, 130, 113, 130, 133, 130, 147, 130, 156, 130, 161, 130, 164, 130, 166, 129, 168, 127, 168, 120, 168, 110, 168, 91, 167, 81, 167, 68, 167]));
|
||||
cb("k", new Uint8Array([149, 63, 147, 68, 143, 76, 136, 89, 126, 106, 114, 123, 100, 136, 86, 147, 72, 153, 57, 155, 45, 152, 36, 145, 29, 131, 26, 117, 26, 104, 27, 93, 30, 86, 35, 80, 45, 77, 62, 80, 88, 96, 113, 116, 130, 131, 140, 142, 145, 149, 148, 153]));
|
||||
cb("l", new Uint8Array([42, 55, 42, 59, 42, 69, 44, 87, 44, 107, 44, 128, 44, 143, 44, 156, 44, 163, 44, 167, 44, 169, 45, 170, 49, 170, 59, 169, 76, 167, 100, 164, 119, 162, 139, 160, 163, 159]));
|
||||
cb("m", new Uint8Array([49, 165, 48, 162, 46, 156, 44, 148, 42, 138, 42, 126, 42, 113, 43, 101, 45, 91, 47, 82, 49, 75, 51, 71, 54, 70, 57, 70, 61, 74, 69, 81, 75, 91, 84, 104, 94, 121, 101, 132, 103, 137, 106, 130, 110, 114, 116, 92, 125, 75, 134, 65, 139, 62, 144, 66, 148, 83, 151, 108, 155, 132, 157, 149]));
|
||||
cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49]));
|
||||
cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62]));
|
||||
cb("p", new Uint8Array([52, 59, 52, 64, 54, 73, 58, 88, 61, 104, 65, 119, 67, 130, 69, 138, 71, 145, 71, 147, 71, 148, 71, 143, 70, 133, 68, 120, 67, 108, 67, 97, 67, 89, 68, 79, 72, 67, 83, 60, 99, 58, 118, 58, 136, 63, 146, 70, 148, 77, 145, 84, 136, 91, 121, 95, 106, 97, 93, 97, 82, 97]));
|
||||
cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67]));
|
||||
cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150]));
|
||||
cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158]));
|
||||
cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146]));
|
||||
cb("u", new Uint8Array([35, 52, 35, 59, 35, 73, 35, 90, 36, 114, 38, 133, 42, 146, 49, 153, 60, 157, 73, 158, 86, 156, 100, 152, 112, 144, 121, 131, 127, 114, 132, 97, 134, 85, 135, 73, 136, 61, 136, 56]));
|
||||
cb("v", new Uint8Array([36, 55, 37, 59, 40, 68, 45, 83, 51, 100, 58, 118, 64, 132, 69, 142, 71, 149, 73, 156, 76, 158, 77, 160, 77, 159, 80, 151, 82, 137, 84, 122, 86, 111, 90, 91, 91, 78, 91, 68, 91, 63, 92, 61, 97, 61, 111, 61, 132, 61, 150, 61, 162, 61]));
|
||||
cb("w", new Uint8Array([33, 58, 34, 81, 39, 127, 44, 151, 48, 161, 52, 162, 57, 154, 61, 136, 65, 115, 70, 95, 76, 95, 93, 121, 110, 146, 119, 151, 130, 129, 138, 84, 140, 56, 140, 45]));
|
||||
cb("x", new Uint8Array([56, 63, 56, 67, 57, 74, 60, 89, 66, 109, 74, 129, 85, 145, 96, 158, 107, 164, 117, 167, 128, 164, 141, 155, 151, 140, 159, 122, 166, 105, 168, 89, 170, 81, 170, 73, 169, 66, 161, 63, 141, 68, 110, 83, 77, 110, 55, 134, 47, 145]));
|
||||
cb("y", new Uint8Array([42, 56, 42, 70, 48, 97, 62, 109, 85, 106, 109, 90, 126, 65, 134, 47, 137, 45, 137, 75, 127, 125, 98, 141, 70, 133, 65, 126, 92, 137, 132, 156, 149, 166]));
|
||||
cb("z", new Uint8Array([29, 62, 35, 62, 43, 62, 63, 62, 87, 62, 110, 62, 125, 62, 134, 62, 138, 62, 136, 63, 122, 68, 103, 77, 85, 91, 70, 107, 59, 120, 50, 132, 47, 138, 43, 143, 41, 148, 42, 151, 53, 155, 80, 157, 116, 158, 146, 158, 163, 158]));
|
||||
exports.getStrokes = function(mode, cb) {
|
||||
if (mode === exports.INPUT_MODE_ALPHA) {
|
||||
cb("a", new Uint8Array([58, 159, 58, 155, 62, 144, 69, 127, 77, 106, 86, 90, 94, 77, 101, 68, 108, 62, 114, 59, 121, 59, 133, 61, 146, 70, 158, 88, 169, 107, 176, 124, 180, 135, 183, 144, 185, 152]));
|
||||
cb("b", new Uint8Array([51, 47, 51, 77, 56, 123, 60, 151, 65, 163, 68, 164, 68, 144, 67, 108, 67, 76, 72, 43, 104, 51, 121, 74, 110, 87, 109, 95, 131, 117, 131, 140, 109, 152, 88, 157]));
|
||||
cb("c", new Uint8Array([153, 62, 150, 62, 145, 62, 136, 62, 123, 62, 106, 65, 85, 70, 65, 75, 50, 82, 42, 93, 37, 106, 36, 119, 36, 130, 40, 140, 49, 147, 61, 153, 72, 156, 85, 157, 106, 158, 116, 158]));
|
||||
cb("d", new Uint8Array([57, 178, 57, 176, 55, 171, 52, 163, 50, 154, 49, 146, 47, 135, 45, 121, 44, 108, 44, 97, 44, 85, 44, 75, 44, 66, 44, 58, 44, 48, 44, 38, 46, 31, 48, 26, 58, 21, 75, 20, 99, 26, 120, 35, 136, 51, 144, 70, 144, 88, 137, 110, 124, 131, 106, 145, 88, 153]));
|
||||
cb("e", new Uint8Array([150, 72, 141, 69, 114, 68, 79, 69, 48, 77, 32, 81, 31, 85, 46, 91, 73, 95, 107, 100, 114, 103, 83, 117, 58, 134, 66, 143, 105, 148, 133, 148, 144, 148]));
|
||||
cb("f", new Uint8Array([157, 52, 155, 52, 148, 52, 137, 52, 124, 52, 110, 52, 96, 52, 83, 52, 74, 52, 67, 52, 61, 52, 57, 52, 55, 52, 52, 52, 52, 54, 52, 58, 52, 64, 54, 75, 58, 97, 59, 117, 60, 130]));
|
||||
cb("g", new Uint8Array([160, 66, 153, 62, 129, 58, 90, 56, 58, 57, 38, 65, 31, 86, 43, 125, 69, 152, 116, 166, 145, 154, 146, 134, 112, 116, 85, 108, 97, 106, 140, 106, 164, 106]));
|
||||
cb("h", new Uint8Array([58, 50, 58, 55, 58, 64, 58, 80, 58, 102, 58, 122, 58, 139, 58, 153, 58, 164, 58, 171, 58, 177, 58, 179, 58, 181, 58, 180, 58, 173, 58, 163, 59, 154, 61, 138, 64, 114, 68, 95, 72, 84, 80, 79, 91, 79, 107, 82, 123, 93, 137, 111, 145, 130, 149, 147, 150, 154, 150, 159]));
|
||||
cb("i", new Uint8Array([89, 48, 89, 49, 89, 51, 89, 55, 89, 60, 89, 68, 89, 78, 89, 91, 89, 103, 89, 114, 89, 124, 89, 132, 89, 138, 89, 144, 89, 148, 89, 151, 89, 154, 89, 156, 89, 157, 89, 158]));
|
||||
cb("j", new Uint8Array([130, 57, 130, 61, 130, 73, 130, 91, 130, 113, 130, 133, 130, 147, 130, 156, 130, 161, 130, 164, 130, 166, 129, 168, 127, 168, 120, 168, 110, 168, 91, 167, 81, 167, 68, 167]));
|
||||
cb("k", new Uint8Array([149, 63, 147, 68, 143, 76, 136, 89, 126, 106, 114, 123, 100, 136, 86, 147, 72, 153, 57, 155, 45, 152, 36, 145, 29, 131, 26, 117, 26, 104, 27, 93, 30, 86, 35, 80, 45, 77, 62, 80, 88, 96, 113, 116, 130, 131, 140, 142, 145, 149, 148, 153]));
|
||||
cb("l", new Uint8Array([42, 55, 42, 59, 42, 69, 44, 87, 44, 107, 44, 128, 44, 143, 44, 156, 44, 163, 44, 167, 44, 169, 45, 170, 49, 170, 59, 169, 76, 167, 100, 164, 119, 162, 139, 160, 163, 159]));
|
||||
cb("m", new Uint8Array([49, 165, 48, 162, 46, 156, 44, 148, 42, 138, 42, 126, 42, 113, 43, 101, 45, 91, 47, 82, 49, 75, 51, 71, 54, 70, 57, 70, 61, 74, 69, 81, 75, 91, 84, 104, 94, 121, 101, 132, 103, 137, 106, 130, 110, 114, 116, 92, 125, 75, 134, 65, 139, 62, 144, 66, 148, 83, 151, 108, 155, 132, 157, 149]));
|
||||
cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49]));
|
||||
cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62]));
|
||||
cb("p", new Uint8Array([29, 47, 29, 55, 29, 75, 29, 110, 29, 145, 29, 165, 29, 172, 29, 164, 30, 149, 37, 120, 50, 91, 61, 74, 72, 65, 85, 61, 103, 61, 118, 63, 126, 69, 129, 76, 130, 87, 126, 98, 112, 108, 97, 114, 87, 116]));
|
||||
cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67]));
|
||||
cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150]));
|
||||
cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158]));
|
||||
cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146]));
|
||||
cb("u", new Uint8Array([35, 52, 35, 59, 35, 73, 35, 90, 36, 114, 38, 133, 42, 146, 49, 153, 60, 157, 73, 158, 86, 156, 100, 152, 112, 144, 121, 131, 127, 114, 132, 97, 134, 85, 135, 73, 136, 61, 136, 56]));
|
||||
cb("v", new Uint8Array([36, 55, 37, 59, 40, 68, 45, 83, 51, 100, 58, 118, 64, 132, 69, 142, 71, 149, 73, 156, 76, 158, 77, 160, 77, 159, 80, 151, 82, 137, 84, 122, 86, 111, 90, 91, 91, 78, 91, 68, 91, 63, 92, 61, 97, 61, 111, 61, 132, 61, 150, 61, 162, 61]));
|
||||
cb("w", new Uint8Array([25, 46, 25, 82, 25, 119, 33, 143, 43, 153, 60, 147, 73, 118, 75, 91, 76, 88, 85, 109, 96, 134, 107, 143, 118, 137, 129, 112, 134, 81, 134, 64, 134, 55]));
|
||||
cb("x", new Uint8Array([56, 63, 56, 67, 57, 74, 60, 89, 66, 109, 74, 129, 85, 145, 96, 158, 107, 164, 117, 167, 128, 164, 141, 155, 151, 140, 159, 122, 166, 105, 168, 89, 170, 81, 170, 73, 169, 66, 161, 63, 141, 68, 110, 83, 77, 110, 55, 134, 47, 145]));
|
||||
cb("y", new Uint8Array([30, 41, 30, 46, 30, 52, 30, 63, 30, 79, 33, 92, 38, 100, 47, 104, 54, 107, 66, 105, 79, 94, 88, 82, 92, 74, 94, 77, 96, 98, 96, 131, 94, 151, 91, 164, 85, 171, 75, 171, 71, 162, 74, 146, 84, 130, 95, 119, 106, 113]));
|
||||
cb("z", new Uint8Array([29, 62, 35, 62, 43, 62, 63, 62, 87, 62, 110, 62, 125, 62, 134, 62, 138, 62, 136, 63, 122, 68, 103, 77, 85, 91, 70, 107, 59, 120, 50, 132, 47, 138, 43, 143, 41, 148, 42, 151, 53, 155, 80, 157, 116, 158, 146, 158, 163, 158]));
|
||||
cb("SHIFT", new Uint8Array([100, 160, 100, 50]));
|
||||
} else if (mode === exports.INPUT_MODE_NUM) {
|
||||
cb("0", new Uint8Array([82, 50, 76, 50, 67, 50, 59, 50, 50, 51, 43, 57, 38, 68, 34, 83, 33, 95, 33, 108, 34, 121, 42, 136, 57, 148, 72, 155, 85, 157, 98, 155, 110, 149, 120, 139, 128, 127, 134, 119, 137, 114, 138, 107, 138, 98, 138, 88, 138, 77, 137, 71, 134, 65, 128, 60, 123, 58]));
|
||||
cb("1", new Uint8Array([100, 50, 100, 160]));
|
||||
cb("2", new Uint8Array([40, 79, 46, 74, 56, 66, 68, 58, 77, 49, 87, 45, 100, 45, 111, 46, 119, 50, 128, 58, 133, 71, 130, 88, 120, 106, 98, 128, 69, 150, 50, 162, 42, 167, 43, 168, 58, 169, 78, 170, 93, 170, 103, 170, 109, 170]));
|
||||
cb("3", new Uint8Array([47, 65, 51, 60, 57, 56, 65, 51, 74, 47, 84, 45, 93, 45, 102, 45, 109, 46, 122, 51, 129, 58, 130, 65, 127, 74, 120, 85, 112, 92, 107, 96, 112, 101, 117, 105, 125, 113, 128, 123, 127, 134, 122, 145, 108, 156, 91, 161, 70, 163, 55, 163]));
|
||||
cb("4", new Uint8Array([37, 58, 37, 60, 37, 64, 37, 69, 37, 75, 37, 86, 37, 96, 37, 105, 37, 112, 37, 117, 37, 122, 37, 126, 37, 128, 38, 129, 40, 129, 45, 129, 48, 129, 53, 129, 67, 129, 85, 129, 104, 129, 119, 129, 129, 129, 136, 129]));
|
||||
cb("5", new Uint8Array([142, 60, 119, 60, 79, 60, 45, 60, 37, 64, 37, 86, 37, 103, 47, 107, 66, 106, 81, 103, 97, 103, 116, 103, 129, 108, 131, 130, 122, 152, 101, 168, 85, 172, 70, 172, 59, 172]));
|
||||
cb("6", new Uint8Array([136, 54, 135, 49, 129, 47, 114, 47, 89, 54, 66, 66, 50, 81, 39, 95, 35, 109, 34, 128, 38, 145, 52, 158, 81, 164, 114, 157, 133, 139, 136, 125, 132, 118, 120, 115, 102, 117, 85, 123]));
|
||||
cb("7", new Uint8Array([47, 38, 48, 38, 53, 38, 66, 38, 85, 38, 103, 38, 117, 38, 125, 38, 129, 38, 134, 41, 135, 47, 135, 54, 135, 66, 131, 93, 124, 126, 116, 149, 109, 161, 105, 168]));
|
||||
cb("8", new Uint8Array([122, 61, 102, 61, 83, 61, 60, 61, 47, 62, 45, 78, 58, 99, 84, 112, 105, 122, 118, 134, 121, 149, 113, 165, 86, 171, 59, 171, 47, 164, 45, 144, 50, 132, 57, 125, 67, 117, 78, 109, 87, 102, 96, 94, 105, 86, 113, 85]));
|
||||
cb("9", new Uint8Array([122, 58, 117, 55, 112, 51, 104, 51, 95, 51, 86, 51, 77, 51, 68, 51, 60, 51, 54, 56, 47, 64, 46, 77, 46, 89, 46, 96, 51, 103, 64, 109, 74, 110, 83, 110, 94, 107, 106, 102, 116, 94, 124, 84, 127, 79, 128, 78, 128, 94, 128, 123, 128, 161, 128, 175]));
|
||||
}
|
||||
cb("\b", new Uint8Array([183, 103, 182, 103, 180, 103, 176, 103, 169, 103, 159, 103, 147, 103, 133, 103, 116, 103, 101, 103, 85, 103, 73, 103, 61, 103, 52, 103, 38, 103, 34, 103, 29, 103, 27, 103, 26, 103, 25, 103, 24, 103]));
|
||||
cb(" ", new Uint8Array([39, 118, 40, 118, 41, 118, 44, 118, 47, 118, 52, 118, 58, 118, 66, 118, 74, 118, 84, 118, 94, 118, 104, 117, 114, 116, 123, 116, 130, 116, 144, 116, 149, 116, 154, 116, 158, 116, 161, 116, 163, 116]));
|
||||
};
|
||||
|
||||
exports.input = function(options) {
|
||||
options = options||{};
|
||||
let input_mode = exports.INPUT_MODE_ALPHA;
|
||||
var text = options.text;
|
||||
if ("string"!=typeof text) text="";
|
||||
|
||||
Bangle.strokes = {};
|
||||
exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
||||
function setupStrokes() {
|
||||
Bangle.strokes = {};
|
||||
exports.getStrokes(input_mode, (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
||||
}
|
||||
setupStrokes();
|
||||
|
||||
var flashToggle = false;
|
||||
const R = Bangle.appRect;
|
||||
|
|
@ -49,6 +70,9 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
|||
var Rx2;
|
||||
var Ry1;
|
||||
var Ry2;
|
||||
let flashInterval;
|
||||
let shift = false;
|
||||
let lastDrag;
|
||||
|
||||
function findMarker(strArr) {
|
||||
if (strArr.length == 0) {
|
||||
|
|
@ -101,10 +125,13 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
|||
*/
|
||||
|
||||
function show() {
|
||||
if (flashInterval) clearInterval(flashInterval);
|
||||
flashInterval = undefined;
|
||||
|
||||
g.reset();
|
||||
g.clearRect(R).setColor("#f00");
|
||||
var n=0;
|
||||
exports.getStrokes((id,s) => {
|
||||
exports.getStrokes(input_mode, (id,s) => {
|
||||
var x = n%6;
|
||||
var y = (n-x)/6;
|
||||
s = g.transformVertices(s, {scale:0.16, x:R.x+x*30-4, y:R.y+y*30-4});
|
||||
|
|
@ -114,39 +141,87 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
|||
});
|
||||
}
|
||||
|
||||
function isInside(rect, e) {
|
||||
return e.x>=rect.x && e.x<rect.x+rect.w
|
||||
&& e.y>=rect.y && e.y<=rect.y+rect.h;
|
||||
}
|
||||
|
||||
function isStrokeInside(rect, stroke) {
|
||||
for(let i=0; i < stroke.length; i+=2) {
|
||||
if (!isInside(rect, {x: stroke[i], y: stroke[i+1]})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function strokeHandler(o) {
|
||||
//print(o);
|
||||
if (!flashInterval)
|
||||
flashInterval = setInterval(() => {
|
||||
flashToggle = !flashToggle;
|
||||
draw();
|
||||
draw(false);
|
||||
}, 1000);
|
||||
if (o.stroke!==undefined) {
|
||||
if (o.stroke!==undefined && o.xy.length >= 6 && isStrokeInside(R, o.xy)) {
|
||||
var ch = o.stroke;
|
||||
if (ch=="\b") text = text.slice(0,-1);
|
||||
else text += ch;
|
||||
g.clearRect(R);
|
||||
else if (ch==="SHIFT") { shift=!shift; Bangle.drawWidgets(); }
|
||||
else text += shift ? ch.toUpperCase() : ch;
|
||||
}
|
||||
lastDrag = undefined;
|
||||
g.clearRect(R);
|
||||
flashToggle = true;
|
||||
draw();
|
||||
draw(false);
|
||||
}
|
||||
|
||||
// Switches between alphabetic and numeric input
|
||||
function cycleInput() {
|
||||
input_mode++;
|
||||
if (input_mode > exports.INPUT_MODE_NUM) input_mode = 0;
|
||||
shift = false;
|
||||
setupStrokes();
|
||||
show();
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
|
||||
Bangle.on('stroke',strokeHandler);
|
||||
g.reset().clearRect(R);
|
||||
show();
|
||||
draw(false);
|
||||
var flashInterval;
|
||||
|
||||
// Create Widget to switch between alphabetic and numeric input
|
||||
WIDGETS.kbswipe={
|
||||
area:"tl",
|
||||
width: 36, // 3 chars, 6*2 px/char
|
||||
draw: function() {
|
||||
g.reset();
|
||||
g.setFont("6x8:2x3");
|
||||
g.setColor("#f00");
|
||||
if (input_mode === exports.INPUT_MODE_ALPHA) {
|
||||
g.drawString(shift ? "ABC" : "abc", this.x, this.y);
|
||||
} else if (input_mode === exports.INPUT_MODE_NUM) {
|
||||
g.drawString("123", this.x, this.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise((resolve,reject) => {
|
||||
var l;//last event
|
||||
Bangle.setUI({mode:"custom", drag:e=>{
|
||||
"ram";
|
||||
if (l) g.reset().setColor("#f00").drawLine(l.x,l.y,e.x,e.y);
|
||||
l = e.b ? e : 0;
|
||||
},touch:() => {
|
||||
if (flashInterval) clearInterval(flashInterval);
|
||||
flashInterval = undefined;
|
||||
show();
|
||||
if (isInside(R, e)) {
|
||||
if (lastDrag) g.reset().setColor("#f00").drawLine(lastDrag.x,lastDrag.y,e.x,e.y);
|
||||
lastDrag = e.b ? e : 0;
|
||||
}
|
||||
},touch:(n,e) => {
|
||||
if (WIDGETS.kbswipe && isInside({x: WIDGETS.kbswipe.x, y: WIDGETS.kbswipe.y, w: WIDGETS.kbswipe.width, h: 24}, e)) {
|
||||
// touch inside widget
|
||||
cycleInput();
|
||||
} else if (isInside(R, e)) {
|
||||
// touch inside app area
|
||||
show();
|
||||
}
|
||||
}, back:()=>{
|
||||
delete WIDGETS.kbswipe;
|
||||
Bangle.removeListener("stroke", strokeHandler);
|
||||
if (flashInterval) clearInterval(flashInterval);
|
||||
Bangle.setUI();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "kbswipe",
|
||||
"name": "Swipe keyboard",
|
||||
"version":"0.05",
|
||||
"version":"0.06",
|
||||
"description": "A library for text input via PalmOS style swipe gestures (beta!)",
|
||||
"icon": "app.png",
|
||||
"type":"textinput",
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
0.01: Moved message icons from messages into standalone library
|
||||
0.02: Added several new icons and colors
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messageicons",
|
||||
"name": "Message Icons",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Library containing a list of icons and colors for apps",
|
||||
"icon": "app.png",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -23,3 +23,4 @@
|
|||
0.17: Use default Bangle formatter for booleans
|
||||
0.18: Improve widget load speed, allow currently recording track to be plotted in openstmap
|
||||
0.19: Fix track plotting code
|
||||
0.20: Automatic translation of some more strings.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ function showMainMenu() {
|
|||
setTimeout(function() {
|
||||
E.showMenu();
|
||||
WIDGETS["recorder"].setRecording(v).then(function() {
|
||||
print("Complete");
|
||||
print(/*LANG*/"Complete");
|
||||
loadSettings();
|
||||
print(settings.recording);
|
||||
showMainMenu();
|
||||
|
|
@ -96,7 +96,7 @@ function showMainMenu() {
|
|||
};
|
||||
var recorders = WIDGETS["recorder"].getRecorders();
|
||||
Object.keys(recorders).forEach(id=>{
|
||||
mainmenu["Log "+recorders[id]().name] = menuRecord(id);
|
||||
mainmenu[/*LANG*/"Log "+recorders[id]().name] = menuRecord(id);
|
||||
});
|
||||
delete recorders;
|
||||
return E.showMenu(mainmenu);
|
||||
|
|
@ -404,7 +404,7 @@ function viewTrack(filename, info) {
|
|||
title: title,
|
||||
miny: min,
|
||||
maxy: max,
|
||||
xlabel : x=>Math.round(x*dur/(60*infn.length))+" min" // minutes
|
||||
xlabel : x=>Math.round(x*dur/(60*infn.length))+/*LANG*/" min" // minutes
|
||||
});
|
||||
g.setFont("6x8",2);
|
||||
g.setFontAlign(0,0,3);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"id": "recorder",
|
||||
"name": "Recorder",
|
||||
"shortName": "Recorder",
|
||||
"version": "0.19",
|
||||
"version": "0.20",
|
||||
"description": "Record GPS position, heart rate and more in the background, then download to your PC.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,outdoors,gps,widget",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Add "from Consec."-setting
|
||||
0.03: Correct how to ignore last triggered alarm
|
||||
0.04: Make "disable alarm" possible on next day; correct alarm filtering; improve settings
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Sleep Log Alarm
|
||||
|
||||
This widget searches for active alarms and raises an own alarm event up to the defined time earlier, if in light sleep or awake phase. Optional the earlier alarm will only be triggered if comming from or in consecutive sleep. The settings of the earlier alarm can be adjusted and it is possible to filter the targeting alarms by time and message. By default the time of the targeting alarm is displayed inside the widget which can be adjusted, too.
|
||||
|
||||
_This widget does not detect sleep on its own and can not create alarms. It requires the [sleeplog](/apps/?id=sleeplog) app and any alarm app that uses [sched](/apps/?id=sched) to be installed._
|
||||
|
||||
---
|
||||
### Settings
|
||||
---
|
||||
|
||||
- __earlier__ | duration to trigger alarm earlier
|
||||
_10min_ / _20min_ / __30min__ / ... / _120min_
|
||||
- __from Consec.__ | only trigger if comming from consecutive sleep
|
||||
_on_ / __off__
|
||||
- __vib pattern__ | vibration pattern for the earlier alarm
|
||||
__..__ / ...
|
||||
- __msg__ | customized message for the earlier alarm
|
||||
__...__ / ...
|
||||
- __msg as prefix__ | use the customized message as prefix to the original message or replace it comlpetely if disabled
|
||||
__on__ / _off_
|
||||
- __disable alarm__ | if enabled the original alarm will be disabled
|
||||
_on_ / __off__
|
||||
- __auto snooze__ | auto snooze option for the earlier alarm
|
||||
__on__ / _off_
|
||||
- __Filter Alarm__ submenu
|
||||
- __time from__ | exclude alarms before this time
|
||||
_0:00_ / _0:15_ / ... / __3:00__ / ... / _24:00_
|
||||
- __time to__ | exclude alarms after this time
|
||||
_0:00_ / _0:15_ / ... / __12:00__ / ... / _24:00_
|
||||
- __msg includes__ | include only alarms including this string in msg
|
||||
__""__ / ...
|
||||
- __Widget__ submenu
|
||||
- __hide__ | completely hide the widget
|
||||
_on_ / __off__
|
||||
- __show time__ | show the time of the targeting alarm
|
||||
__on__ / _off_
|
||||
- __color__ | color of the widget
|
||||
_red_ / __yellow__ / ... / _white_
|
||||
- __Enabled__ | completely en-/disables the background service
|
||||
__on__ / _off_
|
||||
|
||||
---
|
||||
### Worth Mentioning
|
||||
---
|
||||
|
||||
#### Requests, Bugs and Feedback
|
||||
Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)).
|
||||
|
||||
#### Creator
|
||||
Storm64 ([Mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64))
|
||||
|
||||
#### Attributions
|
||||
The app icon is downloaded from [https://icons8.com](https://icons8.com).
|
||||
|
||||
#### License
|
||||
[MIT License](LICENSE)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -0,0 +1,141 @@
|
|||
// load library
|
||||
var sched = require("sched");
|
||||
|
||||
// find next active alarm in range
|
||||
function getNextAlarm(allAlarms, fo, withId) {
|
||||
if (withId) allAlarms = allAlarms.map((a, idx) => {
|
||||
a.idx = idx;
|
||||
return a;
|
||||
});
|
||||
// return next active alarms in range, filter for
|
||||
// active && not timer && not own alarm &&
|
||||
// after from && before to && includes msg
|
||||
var ret = allAlarms.filter(
|
||||
a => a.on && !a.timer && a.id !== "sleeplog" &&
|
||||
a.t >= fo.from && a.t < fo.to && (!fo.msg || a.msg.includes(fo.msg))
|
||||
).map(a => { // add time to alarm
|
||||
a.tTo = sched.getTimeToAlarm(a);
|
||||
return a;
|
||||
}).filter(a => a.tTo // filter non active alarms
|
||||
// sort to get next alarm first
|
||||
).sort((a, b) => a.tTo - b.tTo);
|
||||
// prevent triggering for an already triggered alarm again if available
|
||||
if (fo.lastDate) {
|
||||
var toLast = fo.lastDate - new Date().valueOf() + 1000;
|
||||
if (toLast > 0) ret = ret.filter(a => a.tTo > toLast);
|
||||
}
|
||||
// return first entry
|
||||
return ret[0] || {};
|
||||
}
|
||||
|
||||
exports = {
|
||||
// function to read settings with defaults
|
||||
getSettings: function() {
|
||||
return Object.assign({
|
||||
enabled: true,
|
||||
earlier: 30,
|
||||
fromConsec: false,
|
||||
vibrate: "..",
|
||||
msg: "...\n",
|
||||
msgAsPrefix: true,
|
||||
disableOnAlarm: false, // !!! not available if alarm is at the next day
|
||||
as: true,
|
||||
filter: {
|
||||
from: 3 * 36E5,
|
||||
to: 12 * 36E5,
|
||||
msg: ""
|
||||
},
|
||||
wid: {
|
||||
hide: false,
|
||||
time: true,
|
||||
color: g.theme.dark ? 65504 : 31 // yellow or blue
|
||||
}
|
||||
}, require("Storage").readJSON("sleeplogalarm.settings.json", true) || {});
|
||||
},
|
||||
|
||||
// widget reload function
|
||||
widReload: function() {
|
||||
// abort if trigger object is not available
|
||||
if (typeof (global.sleeplog || {}).trigger !== "object") return;
|
||||
|
||||
// read settings to calculate alarm range
|
||||
var settings = exports.getSettings();
|
||||
|
||||
// set the alarm time
|
||||
this.time = getNextAlarm(sched.getAlarms(), settings.filter).t;
|
||||
|
||||
// abort if no alarm time could be found inside range
|
||||
if (!this.time) return;
|
||||
|
||||
// set widget width if not hidden
|
||||
if (!this.hidden) this.width = 8;
|
||||
|
||||
// insert sleeplogalarm conditions and function
|
||||
sleeplog.trigger.sleeplogalarm = {
|
||||
from: this.time - settings.earlier * 6E4,
|
||||
to: this.time - 1,
|
||||
fn: function (data) {
|
||||
// execute trigger function if on light sleep or awake
|
||||
// and if set if comming from consecutive
|
||||
if ((data.status === 3 || data.status === 2) && !settings.fromConsec ||
|
||||
data.consecutive === 3 || data.prevConsecutive === 3)
|
||||
require("sleeplogalarm").trigger();
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// trigger function
|
||||
trigger: function() {
|
||||
// read settings
|
||||
var settings = exports.getSettings();
|
||||
|
||||
// read all alarms
|
||||
var allAlarms = sched.getAlarms();
|
||||
|
||||
// find first active alarm
|
||||
var alarm = getNextAlarm(sched.getAlarms(), settings.filter, settings.disableOnAlarm);
|
||||
|
||||
// return if no alarm is found
|
||||
if (!alarm) return;
|
||||
|
||||
// get now
|
||||
var now = new Date();
|
||||
|
||||
// get date of the alarm
|
||||
var aDate = new Date(now + alarm.tTo);
|
||||
|
||||
// disable earlier triggered alarm if set
|
||||
if (settings.disableOnAlarm) {
|
||||
// set alarms last to the day it would trigger
|
||||
allAlarms[alarm.idx].last = aDate.getDate();
|
||||
// remove added indexes
|
||||
allAlarms = allAlarms.map(a => {
|
||||
delete a.idx;
|
||||
return a;
|
||||
});
|
||||
}
|
||||
|
||||
// add new alarm for now with data from found alarm
|
||||
allAlarms.push({
|
||||
id: "sleeplog",
|
||||
appid: "sleeplog",
|
||||
on: true,
|
||||
t: ((now.getHours() * 60 + now.getMinutes()) * 60 + now.getSeconds()) * 1000,
|
||||
dow: 127,
|
||||
msg: settings.msg + (settings.msgAsPrefix ? alarm.msg || "" : ""),
|
||||
vibrate: settings.vibrate || alarm.vibrate,
|
||||
as: settings.as,
|
||||
del: true
|
||||
});
|
||||
|
||||
// save date of the alarm to prevent triggering for the same alarm again
|
||||
settings.filter.lastDate = aDate.valueOf();
|
||||
require("Storage").writeJSON("sleeplogalarm.settings.json", settings);
|
||||
|
||||
// write changes
|
||||
sched.setAlarms(allAlarms);
|
||||
|
||||
// trigger sched.js
|
||||
load("sched.js");
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id":"sleeplogalarm",
|
||||
"name":"Sleep Log Alarm",
|
||||
"shortName": "SleepLogAlarm",
|
||||
"version": "0.04",
|
||||
"description": "Enhance your morning and let your alarms wake you up when you are in light sleep.",
|
||||
"icon": "app.png",
|
||||
"type": "widget",
|
||||
"tags": "tool,widget",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"dependencies": {
|
||||
"scheduler": "type",
|
||||
"sleeplog": "app"
|
||||
},
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name": "sleeplogalarm", "url": "lib.js"},
|
||||
{"name": "sleeplogalarm.settings.js", "url": "settings.js"},
|
||||
{"name": "sleeplogalarm.wid.js", "url": "widget.js"}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
(function(back) {
|
||||
// read settings
|
||||
var settings = require("sleeplogalarm").getSettings();
|
||||
|
||||
// write change to storage
|
||||
function writeSetting() {
|
||||
require("Storage").writeJSON("sleeplogalarm.settings.json", settings);
|
||||
}
|
||||
|
||||
// read input from keyboard
|
||||
function readInput(v, cb) {
|
||||
// setTimeout required to load after menu refresh
|
||||
setTimeout((v, cb) => {
|
||||
if (require("Storage").read("textinput")) {
|
||||
g.clear();
|
||||
require("textinput").input({text: v}).then(v => cb(v));
|
||||
} else {
|
||||
E.showAlert(/*LANG*/"No keyboard app installed").then(() => cb());
|
||||
}
|
||||
}, 0, v, cb);
|
||||
}
|
||||
|
||||
// show widget menu
|
||||
function showFilterMenu() {
|
||||
// set menu
|
||||
var filterMenu = {
|
||||
"": {
|
||||
title: "Filter Alarm"
|
||||
},
|
||||
/*LANG*/"< Back": () => showMain(8),
|
||||
/*LANG*/"time from": {
|
||||
value: settings.filter.from / 6E4,
|
||||
step: 10,
|
||||
min: 0,
|
||||
max: 1440,
|
||||
wrap: true,
|
||||
noList: true,
|
||||
format: v => (0|v/60) + ":" + ("" + (v%60)).padStart(2, "0"),
|
||||
onchange: v => {
|
||||
settings.filter.from = v * 6E4;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"time to": {
|
||||
value: settings.filter.to / 6E4,
|
||||
step: 10,
|
||||
min: 0,
|
||||
max: 1440,
|
||||
wrap: true,
|
||||
noList: true,
|
||||
format: v => (0|v/60) + ":" + ("" + (v%60)).padStart(2, "0"),
|
||||
onchange: v => {
|
||||
settings.filter.to = v * 6E4;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"msg includes": {
|
||||
value: settings.filter.msg,
|
||||
format: v => !v ? "" : v.length > 6 ? v.substring(0, 6)+"..." : v,
|
||||
onchange: v => readInput(v, v => {
|
||||
settings.filter.msg = v;
|
||||
writeSetting();
|
||||
showFilterMenu(3);
|
||||
})
|
||||
}
|
||||
};
|
||||
var menu = E.showMenu(filterMenu);
|
||||
}
|
||||
|
||||
// show widget menu
|
||||
function showWidMenu() {
|
||||
// define color values and names
|
||||
var colName = ["red", "yellow", "green", "cyan", "blue", "magenta", "black", "white"];
|
||||
var colVal = [63488, 65504, 2016, 2047, 31, 63519, 0, 65535];
|
||||
|
||||
// set menu
|
||||
var widgetMenu = {
|
||||
"": {
|
||||
title: "Widget Settings"
|
||||
},
|
||||
/*LANG*/"< Back": () => showMain(9),
|
||||
/*LANG*/"hide": {
|
||||
value: settings.wid.hide,
|
||||
onchange: v => {
|
||||
settings.wid.hide = v;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"show time": {
|
||||
value: settings.wid.time,
|
||||
onchange: v => {
|
||||
settings.wid.time = v;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"color": {
|
||||
value: colVal.indexOf(settings.wid.color),
|
||||
min: 0,
|
||||
max: colVal.length -1,
|
||||
wrap: true,
|
||||
format: v => colName[v],
|
||||
onchange: v => {
|
||||
settings.wid.color = colVal[v];
|
||||
writeSetting();
|
||||
}
|
||||
}
|
||||
};
|
||||
var menu = E.showMenu(widgetMenu);
|
||||
}
|
||||
|
||||
// show main menu
|
||||
function showMain(selected) {
|
||||
// set menu
|
||||
var mainMenu = {
|
||||
"": {
|
||||
title: "Sleep Log Alarm",
|
||||
selected: selected
|
||||
},
|
||||
/*LANG*/"< Back": () => back(),
|
||||
/*LANG*/"erlier": {
|
||||
value: settings.earlier,
|
||||
step: 10,
|
||||
min: 10,
|
||||
max: 120,
|
||||
wrap: true,
|
||||
noList: true,
|
||||
format: v => v + /*LANG*/"min",
|
||||
onchange: v => {
|
||||
settings.earlier = v;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"from Consec.": {
|
||||
value: settings.fromConsec,
|
||||
onchange: v => {
|
||||
settings.fromConsec = v;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"vib pattern": require("buzz_menu").pattern(
|
||||
settings.vibrate,
|
||||
v => {
|
||||
settings.vibrate = v;
|
||||
writeSetting();
|
||||
}
|
||||
),
|
||||
/*LANG*/"msg": {
|
||||
value: settings.msg,
|
||||
format: v => !v ? "" : v.length > 6 ? v.substring(0, 6)+"..." : v,
|
||||
onchange: v => readInput(v, v => {
|
||||
settings.msg = v;
|
||||
writeSetting();
|
||||
showMenu(4);
|
||||
})
|
||||
},
|
||||
/*LANG*/"msg as prefix": {
|
||||
value: settings.msgAsPrefix,
|
||||
onchange: v => {
|
||||
settings.msgAsPrefix = v;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"disable alarm": {
|
||||
value: settings.disableOnAlarm,
|
||||
onchange: v => {
|
||||
settings.disableOnAlarm = v;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"auto snooze": {
|
||||
value: settings.as,
|
||||
onchange: v => {
|
||||
settings.as = v;
|
||||
writeSetting();
|
||||
}
|
||||
},
|
||||
/*LANG*/"Filter Alarm": () => showFilterMenu(),
|
||||
/*LANG*/"Widget": () => showWidMenu(),
|
||||
/*LANG*/"Enabled": {
|
||||
value: settings.enabled,
|
||||
onchange: v => {
|
||||
settings.enabled = v;
|
||||
writeSetting();
|
||||
}
|
||||
}
|
||||
};
|
||||
var menu = E.showMenu(mainMenu);
|
||||
}
|
||||
|
||||
// draw main menu
|
||||
showMain();
|
||||
})
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// check if enabled in settings
|
||||
if ((require("Storage").readJSON("sleeplogalarm.settings.json", true) || {enabled: true}).enabled) {
|
||||
// read settings
|
||||
settings = require("sleeplogalarm").getSettings(); // is undefined if used with var
|
||||
|
||||
// insert neccessary settings into widget
|
||||
WIDGETS.sleeplogalarm = {
|
||||
area: "tl",
|
||||
width: 0,
|
||||
time: 0,
|
||||
earlier: settings.earlier,
|
||||
draw: function () {
|
||||
// draw zzz
|
||||
g.reset().setColor(settings.wid.color).drawImage(atob("BwoBD8SSSP4EEEDg"), this.x + 1, this.y);
|
||||
// call function to draw the time of alarm if a alarm is found
|
||||
if (this.time) this.drawTime(this.time + 1);
|
||||
},
|
||||
drawTime: () => {},
|
||||
reload: require("sleeplogalarm").widReload
|
||||
};
|
||||
|
||||
// add function to draw the time of alarm if enabled
|
||||
if (settings.wid.time) WIDGETS.sleeplogalarm.drawTime = function(time) {
|
||||
// directly include Font4x5Numeric
|
||||
g.setFontCustom(atob("CAZMA/H4PgvXoK1+DhPg7W4P1uCEPg/X4O1+AA=="), 46, atob("AgQEAgQEBAQEBAQE"), 5).setFontAlign(1, 1);
|
||||
g.drawString(0|(time / 36E5), this.x + this.width + 1, this.y + 17);
|
||||
g.drawString(0|((time / 36E5)%1 * 60), this.x + this.width + 1, this.y + 23);
|
||||
};
|
||||
|
||||
// load widget
|
||||
WIDGETS.sleeplogalarm.reload();
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ to change (top right or bottom left). It should change color showing
|
|||
it is selected.
|
||||
|
||||
* Swipe up or down to cycle through the info screens that can be displayed
|
||||
when you have finnised tap again towards the centre of the screen to unselect.
|
||||
when you have finished tap again towards the centre of the screen to unselect.
|
||||
|
||||
* Swipe left or right to change the type of info screens displayed (by default
|
||||
there is only one type of data so this will have no effect)
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Rewrite with new interface
|
||||
0.03: Added clock infos to expose timer functionality to clocks.
|
||||
0.04: Improvements of clock infos.
|
||||
0.05: Updated clkinfo icon.
|
||||
|
|
@ -63,10 +63,9 @@
|
|||
} catch(ex){ }
|
||||
}
|
||||
|
||||
var img = atob("GBiBAeAAB+AAB/v/3/v/3/v/3/v/3/v/n/n/H/z+P/48//85//+b//+b//8p//4E//yCP/kBH/oAn/oAX/oAX/oAX/oAX+AAB+AABw==")
|
||||
var smpltmrItems = {
|
||||
name: "Timer",
|
||||
img: img,
|
||||
img: atob("GBiBAAB+AAB+AAAYAAAYAAB+AA3/sA+B8A4AcAwMMBgPGBgPmDAPjDAPzDAPzDP/zDP/zDH/jBn/mBj/GAw8MA4AcAeB4AH/gAB+AA=="),
|
||||
items: [
|
||||
{
|
||||
name: null,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"id": "smpltmr",
|
||||
"name": "Simple Timer",
|
||||
"shortName": "Simple Timer",
|
||||
"version": "0.04",
|
||||
"version": "0.05",
|
||||
"description": "A very simple app to start a timer.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,timer,clkinfo",
|
||||
|
|
|
|||
|
|
@ -17,3 +17,6 @@
|
|||
0.18: Added hasRange to clkinfo.
|
||||
0.19: Added weather condition to clkinfo.
|
||||
0.20: Added weather condition with temperature to clkinfo.
|
||||
0.21: Updated clkinfo icon.
|
||||
0.22: Automatic translation of strings, some left untranslated.
|
||||
>>>>>>> b37fcacd1 (weather - autotranslate strings)
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [
|
|||
{type: "txt", font: "12%", valign: -1, id: "tempUnit", label: "°C"},
|
||||
]},
|
||||
{filly: 1},
|
||||
{type: "txt", font: "6x8", pad: 2, halign: 1, label: "Humidity"},
|
||||
{type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Humidity"},
|
||||
{type: "txt", font: "9%", pad: 2, halign: 1, id: "hum", label: "000%"},
|
||||
{filly: 1},
|
||||
{type: "txt", font: "6x8", pad: 2, halign: -1, label: "Wind"},
|
||||
{type: "txt", font: "6x8", pad: 2, halign: -1, label: /*LANG*/"Wind"},
|
||||
{type: "h", halign: -1, c: [
|
||||
{type: "txt", font: "9%", pad: 2, id: "wind", label: "00"},
|
||||
{type: "txt", font: "6x8", pad: 2, valign: -1, id: "windUnit", label: "km/h"},
|
||||
|
|
@ -27,22 +27,22 @@ var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [
|
|||
]},
|
||||
]},
|
||||
{filly: 1},
|
||||
{type: "txt", font: "9%", wrap: true, height: g.getHeight()*0.18, fillx: 1, id: "cond", label: "Weather condition"},
|
||||
{type: "txt", font: "9%", wrap: true, height: g.getHeight()*0.18, fillx: 1, id: "cond", label: /*LANG*/"Weather condition"},
|
||||
{filly: 1},
|
||||
{type: "h", c: [
|
||||
{type: "txt", font: "6x8", pad: 4, id: "loc", label: "Toronto"},
|
||||
{fillx: 1},
|
||||
{type: "txt", font: "6x8", pad: 4, id: "updateTime", label: "15 minutes ago"},
|
||||
{type: "txt", font: "6x8", pad: 4, id: "updateTime", label: /*LANG*/"15 minutes ago"},
|
||||
]},
|
||||
{filly: 1},
|
||||
]}, {lazy: true});
|
||||
|
||||
function formatDuration(millis) {
|
||||
let pluralize = (n, w) => n + " " + w + (n == 1 ? "" : "s");
|
||||
if (millis < 60000) return "< 1 minute";
|
||||
if (millis < 3600000) return pluralize(Math.floor(millis/60000), "minute");
|
||||
if (millis < 86400000) return pluralize(Math.floor(millis/3600000), "hour");
|
||||
return pluralize(Math.floor(millis/86400000), "day");
|
||||
if (millis < 60000) return /*LANG*/"< 1 minute";
|
||||
if (millis < 3600000) return pluralize(Math.floor(millis/60000), /*LANG*/"minute");
|
||||
if (millis < 86400000) return pluralize(Math.floor(millis/3600000), /*LANG*/"hour");
|
||||
return pluralize(Math.floor(millis/86400000), /*LANG*/"day");
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
|
@ -57,7 +57,7 @@ function draw() {
|
|||
layout.windUnit.label = wind[2] + " " + (current.wrose||'').toUpperCase();
|
||||
layout.cond.label = current.txt.charAt(0).toUpperCase()+(current.txt||'').slice(1);
|
||||
layout.loc.label = current.loc;
|
||||
layout.updateTime.label = `${formatDuration(Date.now() - current.time)} ago`;
|
||||
layout.updateTime.label = `${formatDuration(Date.now() - current.time)} ago`; // How to autotranslate this and similar?
|
||||
layout.update();
|
||||
layout.render();
|
||||
}
|
||||
|
|
@ -77,9 +77,9 @@ function update() {
|
|||
} else {
|
||||
layout.forgetLazyState();
|
||||
if (NRF.getSecurityStatus().connected) {
|
||||
E.showMessage("Weather\nunknown\n\nIs Gadgetbridge\nweather\nreporting set\nup on your\nphone?");
|
||||
E.showMessage(/*LANG*/"Weather\nunknown\n\nIs Gadgetbridge\nweather\nreporting set\nup on your\nphone?");
|
||||
} else {
|
||||
E.showMessage("Weather\nunknown\n\nGadgetbridge\nnot connected");
|
||||
E.showMessage(/*LANG*/"Weather\nunknown\n\nGadgetbridge\nnot connected");
|
||||
NRF.on("connect", update);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
//FIXME ranges are somehow arbitrary
|
||||
var weatherItems = {
|
||||
name: "Weather",
|
||||
img: atob("GBiBAf+///u5//n7//8f/9wHP8gDf/gB//AB/7AH/5AcP/AQH/DwD/uAD84AD/4AA/wAAfAAAfAAAfAAAfgAA/////+bP/+zf/+zfw=="),
|
||||
img: atob("GBiBAABAAARGAAYEAADgACP4wDf8gAf+AA/+AE/4AG/jwA/v4A8P8AR/8DH/8AH//AP//g///g///g///gf//AAAAABkwABMgABMgA=="),
|
||||
items: [
|
||||
{
|
||||
name: "conditionWithTemperature",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "weather",
|
||||
"name": "Weather",
|
||||
"version": "0.20",
|
||||
"version": "0.22",
|
||||
"description": "Show Gadgetbridge weather report",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ exports.load = function() {
|
|||
// actual menu
|
||||
var menu = [{
|
||||
name: "Bangle",
|
||||
img: atob("GBiBAf8B//4B//4B//4B//4A//x4//n+f/P/P+fPn+fPn+fP3+/Px+/Px+fn3+fzn+f/n/P/P/n+f/x4//4A//4B//4B//4B//8B/w=="),
|
||||
img: atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA=="),
|
||||
items: [
|
||||
{ name : "Battery",
|
||||
hasRange : true,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ exports.show = function() {
|
|||
|
||||
/// Remove any intervals/handlers/etc that we might have added. Does NOT re-show widgets that were hidden
|
||||
exports.cleanup = function() {
|
||||
delete exports.autohide;
|
||||
delete Bangle.appRect;
|
||||
if (exports.swipeHandler) {
|
||||
Bangle.removeListener("swipe", exports.swipeHandler);
|
||||
|
|
@ -50,11 +51,13 @@ exports.cleanup = function() {
|
|||
|
||||
/** Put widgets offscreen, and allow them to be swiped
|
||||
back onscreen with a downwards swipe. Use .show to undo.
|
||||
First parameter controls automatic hiding time, 0 equals not hiding at all.
|
||||
Default value is 2000ms until hiding.
|
||||
Bangle.js 2 only at the moment. */
|
||||
exports.swipeOn = function() {
|
||||
exports.swipeOn = function(autohide) {
|
||||
exports.cleanup();
|
||||
if (!global.WIDGETS) return;
|
||||
|
||||
exports.autohide=autohide===undefined?2000:autohide;
|
||||
/* TODO: maybe when widgets are offscreen we don't even
|
||||
store them in an offscreen buffer? */
|
||||
|
||||
|
|
@ -125,11 +128,13 @@ exports.swipeOn = function() {
|
|||
clearTimeout(exports.hideTimeout);
|
||||
delete exports.hideTimeout;
|
||||
}
|
||||
if (ud>0 && offset<0) anim(4, function() {
|
||||
let cb;
|
||||
if (exports.autohide > 0) cb = function() {
|
||||
exports.hideTimeout = setTimeout(function() {
|
||||
anim(-4);
|
||||
}, 2000);
|
||||
});
|
||||
}, exports.autohide);
|
||||
}
|
||||
if (ud>0 && offset<0) anim(4, cb);
|
||||
if (ud<0 && offset>-24) anim(-4);
|
||||
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue