Merge branch 'espruino:master' into master

master
awkirk71 2021-05-16 14:22:40 +01:00
commit 6c20f9c96a
27 changed files with 1025 additions and 227 deletions

View File

@ -2857,10 +2857,10 @@
"name": "Test User Input",
"shortName":"Test User Input",
"icon": "app.png",
"version":"0.05",
"description": "Basic app to test the bangle.js input interface. It displays the user action in text, an option round element or an on/off switch image for swipe movements.",
"version":"0.06",
"description": "App to test the bangle.js input interface. It displays the user action in text, circle buttons or on/off switch UI elements.",
"readme": "README.md",
"tags": "input,interface,buttons,touch",
"tags": "input,interface,buttons,touch,UI",
"storage": [
{"name":"testuserinput.app.js","url":"app.js"},
{"name":"testuserinput.img","url":"app-icon.js","evaluate":true}
@ -3044,7 +3044,7 @@
"name": "Gadgetbridge Music Controls",
"shortName":"Music Controls",
"icon": "icon.png",
"version":"0.03",
"version":"0.05",
"description": "Control the music on your Gadgetbridge-connected phone",
"tags": "tools,bluetooth,gadgetbridge,music",
"type":"app",
@ -3085,8 +3085,8 @@
{ "id": "kitchen",
"name": "Kitchen Combo",
"icon": "kitchen.png",
"version":"0.06",
"description": "Combination of the stepo, walkersclock, arrow and waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
"version":"0.10",
"description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
"tags": "tool,outdoors,gps",
"type":"clock",
"readme": "README.md",
@ -3096,10 +3096,13 @@
{"name":"stepo.kit.js","url":"stepo.kit.js"},
{"name":"gps.kit.js","url":"gps.kit.js"},
{"name":"digi.kit.js","url":"digi.kit.js"},
{"name":"heart.kit.js","url":"heart.kit.js"},
{"name":"swatch.kit.js","url":"swatch.kit.js"},
{"name":"compass.kit.js","url":"compass.kit.js"},
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
],
"data": [
{"name":"waypoints.json","url":"waypoints.json"}
]
},
{ "id": "qmsched",
@ -3152,7 +3155,7 @@
"id": "omnitrix",
"name":"Omnitrix",
"icon":"omnitrix.png",
"version": "1.0",
"version": "0.01",
"readme": "README.md",
"description": "An Omnitrix Showpiece",
"tags": "game",
@ -3166,7 +3169,7 @@
"name": "Bat Clock",
"shortName":"Bat Clock",
"icon": "bat-clock.png",
"version":"1.0",
"version":"0.01",
"description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.",
"tags": "clock",
"type": "clock",
@ -3175,5 +3178,19 @@
{"name":"batclock.app.js","url":"bat-clock.app.js"},
{"name":"batclock.img","url":"bat-clock.icon.js","evaluate":true}
]
},
{ "id":"doztime",
"name":"Dozenal Time",
"shortName":"Dozenal Time",
"icon":"app.png",
"version":"0.01",
"description":"A dozenal Holocene calendar and dozenal diurnal clock",
"tags":"clock",
"type":"clock",
"readme": "README.md",
"storage": [
{"name":"doztime.app.js","url":"app.js"},
{"name":"doztime.img","url":"app-icon.js","evaluate":true}
]
}
]

1
apps/doztime/ChangeLog Normal file
View File

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

14
apps/doztime/README.md Normal file
View File

@ -0,0 +1,14 @@
Dozenal Time
============
A dozenal Holocene calendar and a dozenal diurnal clock. For information about them, go to https://dozenal.ae-web.ca/pdf/dozenal-calendar.pdf and https://dozenal.ae-web.ca/pdf/about-short.pdf. They've been in use for some years.
In the dozenal number base, ten and eleven are single digits, and 10 is a dozen. The clock simply divides the day by successive powers of a dozen. The day or parts of it may be divided easily into halves, thirds, quarters, sixths, or twelfths (dozenths). There is no conglomeration of bases two, ten, twelve, and sixty, as in the current system of time measurement.
The annual calendar has a dozen months of 5 weeks each, each week having 6 days. The 5 or 6 days beyond 360 (dozenal 260) are added where they keep the season beginnings the most accurate.
The year itself begins on the December solstice. Because that always happens, there is no need of a leap-year rule to keep the seasons from drifting.
The epoch (year numbering) begins in the last year when the perihelion coincided with the June solstice, near the beginning of the Holocene era. That astronomical basis makes the calendar free from politics, religion, or geography.
While the year number remains cardinal, BTN5 toggles between cardinal and ordinal for the rest of the calendar segments. BTN4 adds or removes a quickly changing digit to or from the clock.

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

@ -0,0 +1 @@
require("heatshrink").decompress(atob("lEowggdkUiCKIADCJcCkUjmYACmUikAlKB4ImDAoQSJkYhBFAQECAQI5HBQU//4AC+YUCHowzBCQfzAYYKCEw8vEgYqD+QoGgQbBHAYADCwIoBCYkiEwhPEBAIoBHgY6BExHyHwQhBFAQ6BkYTHDgcyHgcCHRZlDCYQsBTYg6GDAJQDPoI6LAAIPBCYRiHHQhkDCYRiHHQhkCCYKKBCYzzBA4yMBCYTVEGYITEBYITZHY5PHUAJjITIJjHRZINBIYoTDWZAoFWYbbJFALbHgUyX4oPDXIcjMQITBmZkHFYszCYZkJMQoTCKAQ8IHQZOCHgYoKkQ6DHgYoEcIgmBHQg8CFAIPCCYfzBQQSEFAbrFCQImHFAQUCkczmYECAQISGHoYzBAAQFCCRA9BEwYoDHI4pFAAgRLCooRPABg="))

221
apps/doztime/app.js Normal file
View File

@ -0,0 +1,221 @@
// Positioning values for graphics buffers
const g_height = 80; // total graphics height
const g_x_off = 16; // position from left
const g_y_off = (240 - g_height)/2; // vertical center for graphics region
const g_width = 240 - 2 * g_x_off; // total graphics width
const g_height_d = 32; // height of date region
const g_y_off_d = 0; // y position of date region within graphics region
const spacing = 0; // space between date and time in graphics region
const g_y_off_t = g_y_off_d + g_height_d + spacing; // y position of time within graphics region
const g_height_t = 48; // height of time region
// Other vars
const A1 = [30,30,30,30,31,31,31,31,31,31,30,30];
const B1 = [30,30,30,30,30,31,31,31,31,31,30,30];
const B2 = [30,30,30,30,31,31,31,31,31,30,30,30];
const timeColour = "#f2f2f2";
const dateColours = ["#ff0000","#ffa500","#ffff00","#00b800","#0000ff","#ff00ff","#ff0080"];
const calen10 = {"size":32,"pt0":[32-g_x_off,16],"step":[20,0],"dx":-4.5,"dy":-4.5}; // positioning for usual calendar line
const calen7 = {"size":32,"pt0":[62-g_x_off,16],"step":[20,0],"dx":-4.5,"dy":-4.5}; // positioning for S-day calendar line
const time5 = {"size":48,"pt0":[64-g_x_off,24],"step":[30,0],"dx":-6.5,"dy":-6.5}; // positioning for lull time line; was 64
const time6 = {"size":48,"pt0":[48-g_x_off,24],"step":[30,0],"dx":-6.5,"dy":-6.5}; // positioning for twinkling time line
const baseYear = 11584;
const baseDate = Date(2020,11,21); // month values run from 0 to 11
let accum = new Date(baseDate.getTime());
let sequence = [];
let timeActiveUntil;
let addTimeDigit = false;
let dateFormat = false;
let lastX = 999999999;
let res = {};
//var last_time_log = 0;
// Date and time graphics buffers
var dateColour = "#ffffff"; // override later
var g_d = Graphics.createArrayBuffer(g_width,g_height_d,1,{'msb':true});
var g_t = Graphics.createArrayBuffer(g_width,g_height_t,1,{'msb':true});
// Set screen mode and function to write graphics buffers
Bangle.setLCDMode();
g.clear(); // start with blank screen
g.flip = function()
{
g.setColor(dateColour);
g.drawImage(
{
width:g_width,
height:g_height_d,
buffer:g_d.buffer
}, g_x_off, g_y_off + g_y_off_d);
g.setColor(timeColour);
g.drawImage(
{
width:g_width,
height:g_height_t,
buffer:g_t.buffer
}, g_x_off, g_y_off + g_y_off_t);
};
setWatch(function(){ modeTime(); }, BTN1, {repeat:true} );
setWatch(function(){ Bangle.showLauncher(); }, BTN2, { repeat: false, edge: "falling" });
setWatch(function(){ modeWeather(); }, BTN3, {repeat:true});
setWatch(function(){ toggleTimeDigits(); }, BTN4, {repeat:true});
setWatch(function(){ toggleDateFormat(); }, BTN5, {repeat:true});
function buildSequence(targ){
for(let i=0;i<targ.length;++i){
sequence.push(new Date(accum.getTime()));
accum.setDate(accum.getDate()+targ[i]);
}
}
buildSequence(B2);
buildSequence(B2);
buildSequence(A1);
buildSequence(B1);
buildSequence(B2);
buildSequence(B2);
buildSequence(A1);
buildSequence(B1);
buildSequence(B2);
buildSequence(B2);
buildSequence(A1);
buildSequence(B1);
buildSequence(B2);
function getDate(dt){
let index = sequence.findIndex(n => n > dt)-1;
let year = baseYear+parseInt(index/12);
let month = index % 12;
let day = parseInt((dt-sequence[index])/86400000);
let colour = dateColours[day % 6];
if(day==30){ colour=dateColours[6]; }
return({"year":year,"month":month,"day":day,"colour":colour});
}
function toggleTimeDigits(){
addTimeDigit = !addTimeDigit;
modeTime();
}
function toggleDateFormat(){
dateFormat = !dateFormat;
modeTime();
}
function formatDate(res,dateFormat){
let yyyy = res.year.toString(12);
calenDef = calen10;
if(!dateFormat){ //ordinal format
let mm = ("0"+(res.month+1).toString(12)).substr(-2);
let dd = ("0"+(res.day+1).toString(12)).substr(-2);
if(res.day==30){
calenDef = calen7;
let m = ((res.month+1).toString(12)).substr(-2);
return(yyyy+"-"+"S"+m); // ordinal format
}
return(yyyy+"-"+mm+"-"+dd);
}
let m = res.month.toString(12); // cardinal format
let w = parseInt(res.day/6);
let d = res.day%6;
//return(yyyy+"-"+res.month+"-"+w+"-"+d);
return(yyyy+"-"+m+"-"+w+"-"+d);
}
function writeDozTime(text,def,colour){
let pts = def.pts;
let x=def.pt0[0];
let y=def.pt0[1];
g_t.clear();
g_t.setFont("Vector",def.size);
for(let i in text){
if(text[i]=="a"){ g_t.setFontAlign(0,0,2); g_t.drawString("2",x+def.dx,y+def.dy); }
else if(text[i]=="b"){ g_t.setFontAlign(0,0,2); g_t.drawString("3",x+def.dx,y+def.dy); }
else{ g_t.setFontAlign(0,0,0); g_t.drawString(text[i],x,y); }
x = x+def.step[0];
y = y+def.step[1];
}
}
function writeDozDate(text,def,colour){
dateColour = colour;
let pts = def.pts;
let x=def.pt0[0];
let y=def.pt0[1];
g_d.clear();
g_d.setFont("Vector",def.size);
for(let i in text){
if(text[i]=="a"){ g_d.setFontAlign(0,0,2); g_d.drawString("2",x+def.dx,y+def.dy); }
else if(text[i]=="b"){ g_d.setFontAlign(0,0,2); g_d.drawString("3",x+def.dx,y+def.dy); }
else{ g_d.setFontAlign(0,0,0); g_d.drawString(text[i],x,y); }
x = x+def.step[0];
y = y+def.step[1];
}
}
// Functions for time mode
function drawTime()
{
let dt = new Date();
let date = "";
let timeDef;
let x = 0;
dt.setDate(dt.getDate());
if(addTimeDigit){
x =
10368*dt.getHours()+172.8*dt.getMinutes()+2.88*dt.getSeconds()+0.00288*dt.getMilliseconds();
let msg = "00000"+Math.floor(x).toString(12);
let time = msg.substr(-5,3)+"."+msg.substr(-2);
let wait = 347*(1-(x%1));
timeDef = time6;
} else {
x =
864*dt.getHours()+14.4*dt.getMinutes()+0.24*dt.getSeconds()+0.00024*dt.getMilliseconds();
let msg = "0000"+Math.floor(x).toString(12);
let time = msg.substr(-4,3)+"."+msg.substr(-1);
let wait = 4167*(1-(x%1));
timeDef = time5;
}
if(lastX > x){ res = getDate(dt); } // calculate date once at start-up and once when turning over to a new day
date = formatDate(res,dateFormat);
if(dt<timeActiveUntil)
{
// Write to background buffers, then display on screen
writeDozDate(date,calenDef,res.colour);
writeDozTime(time,timeDef,timeColour);
g.flip();
// Ready next interval
setTimeout(drawTime,wait);
}
else
{
// Clear screen
g_d.clear();
g_t.clear();
g.flip();
}
lastX = x;
}
function modeTime()
{
timeActiveUntil = new Date();
timeActiveUntil.setDate(timeActiveUntil.getDate());
timeActiveUntil.setSeconds(timeActiveUntil.getSeconds()+15);
//Bangle.setLCDPower(true);
clearTimeout();
drawTime();
}
// Time-logging function
/*function logTime(label)
{
var d = new Date();
var t = d.getTime();
var diff_test = t - last_time_log;
last_time_log = t;
console.log(label + " at time: " + t + ", since last: " + diff_test);
}*/
// Functions for weather mode - TODO
function drawWeather() {}
function modeWeather() {}
// Start time on twist
Bangle.on('twist', function() {
modeTime();
});

BIN
apps/doztime/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,3 +1,5 @@
0.01: Initial version
0.02: Increase text brightness, improve controls, (try to) reduce memory usage
0.03: Only auto-start if active app is a clock, auto close after 1 hour of inactivity
0.04: Setting to disable touch controls, minor bugfix
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker

View File

@ -16,10 +16,16 @@ Download the [latest Gadgetbridge for Android here](https://f-droid.org/packages
## Settings
The app can automatically load when you play music and close when the music stops.
You can change this under `Settings`->`App/Widget Settings`->`Music Controls`.
You can change these under `Settings`->`App/Widget Settings`->`Music Controls`.
**Auto start**:
Automatically load the app when you play music and close when the music stops.
(If the app opened automatically, it closes after music has been paused for 5 minutes.)
**Simple button**:
Disable double/triple pressing Button 2: always simply toggle play/pause.
(For music players which handle multiple button presses themselves.)
## Controls
### Buttons

View File

@ -38,7 +38,7 @@ function fadeOut() {
if (!Bangle.isLCDOn() || !fade) {
return;
}
drawMusic();
drawMusic(false); // don't clear: draw over existing text to prevent flicker
setTimeout(fadeOut, 500);
}
function brightness() {
@ -127,7 +127,7 @@ function f2hex(f) {
return ("00"+(Math.round(f*255)).toString(16)).substr(-2);
}
/**
* @param name
* @param {string} name - musicinfo property "num"/"artist"/"album"/"track"
* @return {string} Semi-random color to use for given info
*/
function infoColor(name) {
@ -138,16 +138,13 @@ function infoColor(name) {
s = 0;
} else {
// make color depend deterministically on info
let code = 0;
let code = textCode(info[name]);
switch(name) {
case "track":
code += textCode(info.track);
// fallthrough: also use album+artist
case "album":
case "track": // also use album
code += textCode(info.album);
// fallthrough: also use artist
default:
code += textCode(info[name]);
// fallthrough
case "album": // also use artist
code += textCode(info.artist);
}
h = code%360;
s = 0.7;
@ -173,7 +170,6 @@ function trackColor() {
////////////////////
/**
* Draw date and time
* @return {*}
*/
function drawDateTime() {
const now = new Date;
@ -208,8 +204,9 @@ function drawDateTime() {
/**
* Draw track number and total count
* @param {boolean} clr - Clear area before redrawing?
*/
function drawNum() {
function drawNum(clr) {
let num = "";
if ("n" in info && info.n>0) {
num = "#"+info.n;
@ -219,9 +216,11 @@ function drawNum() {
}
g.reset();
g.setFont("Vector", 30)
.setFontAlign(1, -1) // top right
.clearRect(225, 30, 120, 60)
.drawString(num, 225, 30);
.setFontAlign(1, -1); // top right
if (clr) {
g.clearRect(225, 30, 120, 60);
}
g.drawString(num, 225, 30);
}
/**
* Clear rectangle used by track title
@ -231,8 +230,9 @@ function clearTrack() {
}
/**
* Draw track title
* @param {boolean} clr - Clear area before redrawing?
*/
function drawTrack() {
function drawTrack(clr) {
let size = fitText(info.track);
if (size<25) {
// the title is too long: start the scroller
@ -249,7 +249,9 @@ function drawTrack() {
g.setFont("Vector", size)
.setFontAlign(0, 1) // center bottom
.setColor(trackColor());
clearTrack();
if (clr) {
clearTrack();
}
g.drawString(info.track, 119, 109);
}
/**
@ -269,8 +271,9 @@ function drawScroller() {
/**
* Draw track artist and album
* @param {boolean} clr - Clear area before redrawing?
*/
function drawArtistAlbum() {
function drawArtistAlbum(clr) {
// we just use small enough fonts to make these always fit
// calculate stuff before clear+redraw
const aCol = infoColor("artist");
@ -284,7 +287,9 @@ function drawArtistAlbum() {
bSiz = 20;
}
g.reset();
g.clearRect(0, 120, 240, 189);
if (clr) {
g.clearRect(0, 120, 240, 189);
}
let top = 124;
if (info.artist) {
g.setFont("Vector", aSiz)
@ -377,10 +382,14 @@ function drawControls() {
controlState = stat;
}
function drawMusic() {
drawNum();
drawTrack();
drawArtistAlbum();
/**
* @param {boolean} [clr=true] Clear area before redrawing?
*/
function drawMusic(clr) {
clr = !(clr===false); // undefined means yes
drawNum(clr);
drawTrack(clr);
drawArtistAlbum(clr);
}
////////////////////////
@ -388,7 +397,7 @@ function drawMusic() {
///////////////////////
/**
* Update music info
* @param e
* @param {Object} e - Gadgetbridge musicinfo event
*/
function musicInfo(e) {
info = e;
@ -408,7 +417,11 @@ function musicInfo(e) {
}
}
let tPxt, tIxt;
let tPxt, tIxt; // Timeouts to eXiT when Paused/Inactive for too long
/**
* Update music state
* @param {Object} e - Gadgetbridge musicstate event
*/
function musicState(e) {
stat = e.state;
// if paused for five minutes, load the clock
@ -444,6 +457,7 @@ function musicState(e) {
}
}
if (Bangle.isLCDOn()) {
drawMusic(false); // redraw in case we were fading out but resumed play
drawControls();
}
}
@ -471,11 +485,19 @@ function startButtonWatches() {
tPress = setTimeout(() => {Bangle.showLauncher();}, 3000);
}
}, BTN2, {repeat: true, edge: "rising"});
setWatch(() => {
nPress++;
clearTimeout(tPress);
tPress = setTimeout(handleButton2Press, 500);
}, BTN2, {repeat: true, edge: "falling"});
const s = require("Storage").readJSON("gbmusic.json", 1) || {};
if (s.simpleButton) {
setWatch(() => {
clearTimeout(tPress);
togglePlay();
}, BTN2, {repeat: true, edge: "falling"});
} else {
setWatch(() => {
nPress++;
clearTimeout(tPress);
tPress = setTimeout(handleButton2Press, 500);
}, BTN2, {repeat: true, edge: "falling"});
}
}
function handleButton2Press() {
tPress = null;
@ -498,7 +520,7 @@ function handleButton2Press() {
let tCommand = {};
/**
* Send command and highlight corresponding control
* @param command "play/pause/next/previous/volumeup/volumedown"
* @param {string} command - "play"/"pause"/"next"/"previous"/"volumeup"/"volumedown"
*/
function sendCommand(command) {
Bluetooth.println(JSON.stringify({t: "music", n: command}));
@ -519,6 +541,7 @@ function togglePlay() {
}
function startTouchWatches() {
Bangle.on("touch", side => {
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
switch(side) {
case 1:
sendCommand(stat==="play" ? "pause" : "previous");
@ -531,6 +554,7 @@ function startTouchWatches() {
}
});
Bangle.on("swipe", dir => {
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
sendCommand(dir===1 ? "previous" : "next");
});
}

View File

@ -4,35 +4,42 @@
(function(back) {
const SETTINGS_FILE = "gbmusic.json",
storage = require("Storage"),
translate = require("locale").translate
translate = require("locale").translate;
// initialize with default settings...
let s = {
autoStart: true,
}
simpleButton: false,
};
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
const saved = storage.readJSON(SETTINGS_FILE, 1) || {}
const saved = storage.readJSON(SETTINGS_FILE, 1) || {};
for(const key in saved) {
s[key] = saved[key]
s[key] = saved[key];
}
// creates a function to safe a specific setting, e.g. save('autoStart')(true)
function save(key) {
return function(value) {
s[key] = value
storage.write(SETTINGS_FILE, s)
return function (value) {
s[key] = value;
storage.write(SETTINGS_FILE, s);
}
}
const menu = {
const yesNo = (v) => translate(v ? "Yes" : "No");
let menu = {
"": {"title": "Music Control"},
"< Back": back,
"Auto start": {
value: s.autoStart,
format: v => translate(v ? "Yes" : "No"),
onchange: save("autoStart"),
}
}
E.showMenu(menu)
})
};
menu[translate("< Back")] = back;
menu[translate("Auto start")] = {
value: !!s.autoStart,
format: yesNo,
onchange: save("autoStart"),
};
menu[translate("Simple button")] = {
value: !!s.simpleButton,
format: yesNo,
onchange: save("simpleButton"),
};
E.showMenu(menu);
});

View File

@ -4,3 +4,7 @@
0.04: Added stopwatch face
0.05: Stopwatch, hide hours if 0, fixed flicker when stopped, updated README issues
0.06: Reduced memory footprint of compass, used direct screen access rather than arrayBuffer
0.07: Added error codes if dependancies are missing
0.08: Improved error handling for missing firmware features, added template app.kit.js
0.09: Added heart rate monitor app
0.10: Converted Stepo to use direct screen writes, added a Trip Counter feature to stepo

View File

@ -1,10 +1,10 @@
# Kitchen Combo - a multiclock format of the waypointer, walksersclock, stepo and arrow apps.
# Kitchen Combo - a multiclock format of the waypointer, walkersclock, stepo, stopwatch, heartrate and arrow apps.
![](screenshot_kitchen.jpg)
*...everything but the kitchen sink..*
NOTE: This app require Bangle firmware 2.08.187 or later.
NOTE: This app requires Bangle firmware 2.08.187 or later.
The app is aimed at navigation whilst walking. Please note that it
would be foolish in the extreme to rely on this as your only
@ -44,13 +44,16 @@ The following buttons depend on which face is currently in use
## Stepo
![](screenshot_stepo.jpg)
- Requires one of the pedominter widgets to be installed
- Displays the time in large font
- Display current step count in a doughnut gauge
- Show step count in the middle of the doughnut gauge
- The gauge show percentage of steps out of a goal of 10000 steps
- When the battery is less than 25% the doughnut turns red
- Use BTN1 to switch to the Trip Counter, use long press to reset Trip Counter
- Use BTN3 to switch to the next app
## GPS
![](screenshot_gps.jpg)
- Use BTN1 long press to switch the GPS on or off
@ -65,10 +68,16 @@ The following buttons depend on which face is currently in use
- Use BTN3 to switch to the next app.
## Swatch
![](screenshot_swatch.jpg)
- A simple stopwatch
- BTN1 - start, stop
- BTN2 - lap if the timer is running, reset if the timer is stopped
## Heart
![](screenshot_heart.jpg)
- A simple heart rate monitor, at present the app is just showing the raw value from HRM.bpm
- BTN1, long press, turn heart rate monitor on / off
## Waypointer
- Use BTN1 to select previous waypoint (when GPS is on)
- Use BTN2 to select the next waypoint (when GPS is on)
@ -217,12 +226,31 @@ I have settled on directly writing to the screen using the Graphics
object (g.) for the compass App. This creates a bit of flicker when
the arrow moves but is more reliable than using the ArrayBuffer.
v0.09: Since adding the heart rate monitor I have noticed that I can
sometimes can a memory error when switch through the Apps back to the
Stepo App. I think this can be cured by statically allocating the
ArrayBuffer for stepo rather than using new everytime you switch back
into the stepo watch face. The problem is that the bangle memory
management / defragmentation is quite slow to run.
### Issues
v0.10: Revisited having a display buffer for the stepo part of the App.
Now use direct screen writing as it means less memory allocation and
reduces chance of getting a memory error on switching watch faces.
### Error Codes
The following error codes will be displayed if one of the dependancies is not met.
* E-STEPS - no pedomintor widget has been installed, please install the widpedom or the activepedom widgets
* E-CALIB - no compass calibration data was found, see 'Compass Calibration'
* E-FW - require firmware 2v08.187 or later to detect gps and compass power status
### Issues / Future enhancements
* GPS time display shows GMT and not BST, needs localising
* Occassional buzzing after 2-3 days of use, seems to disappear after
a reset to the launcher menu. Needs investigation
* Need to gracefully handle incorrect firmware
* Need to gracefully handle missing compass calibration data
* Need to gracefully handle missing steps widget
* Automatically switch the GPS power setting from Super-E to PSMOO 10
seconds after the LCD goes off. At present I just rely on using
the GPSSetup app and set the GPS power mode that I want.
* Add a small graph to the heart rate monitor app

55
apps/kitchen/app.kit.js Normal file
View File

@ -0,0 +1,55 @@
// simple template
(() => {
function getFace(){
var intervalRefSec;
var prevTime;
const Y_TIME = 30;
const Y_ACTIVITY = 116;
function init(gps,sw) {
prevTime = "";
g.clear();
}
function freeResources() {
prevTime = undefined;
}
function startTimer() {
draw();
intervalRefSec = setInterval(draw, 5000);
}
function stopTimer() {
if (intervalRefSec) { intervalRefSec = clearInterval(intervalRefSec); }
}
function onButtonShort(btn) {}
function onButtonLong(btn) {}
function draw() {
var d = new Date();
var da = d.toString().split(" ");
var time = da[4].substr(0,5);
if (time !== prevTime) {
prevTime = time;
g.setColor(0);
g.fillRect(0, Y_TIME, 239, Y_ACTIVITY -1);
g.setColor(1,1,1);
g.setFont("Vector",80);
g.setFontAlign(0,-1);
g.drawString(time, 120, Y_TIME);
g.setFont("Vector",26);
g.drawString("Hello World", 120, Y_ACTIVITY);
}
}
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
}
return getFace;
})();

View File

@ -16,7 +16,7 @@
//console.log(o);
}
function init(gps,sw) {
function init(gps,sw, hrm) {
showMem("compass init() START");
gpsObject = gps;
intervalRefSec = undefined;
@ -49,13 +49,13 @@
loc = undefined;
CALIBDATA = undefined;
wp = undefined;
if (Bangle.isCompassOn()) Bangle.setCompassPower(0);
if (Bangle.isCompassOn !== undefined && Bangle.isCompassOn()) Bangle.setCompassPower(0);
showMem("compass freeResources() END");
}
function startTimer() {
log_debug("startTimer()");
if (!Bangle.isCompassOn()) Bangle.setCompassPower(1);
if (Bangle.isCompassOn !== undefined && !Bangle.isCompassOn()) Bangle.setCompassPower(1);
resetPrevious();
draw();
intervalRefSec = setInterval(draw, 500);
@ -63,8 +63,8 @@
function stopTimer() {
log_debug("stopTimer()");
if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
if (Bangle.isCompassOn()) Bangle.setCompassPower(0);
if (intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
if (Bangle.isCompassOn !== undefined && Bangle.isCompassOn()) Bangle.setCompassPower(0);
}
function showMem(msg) {
@ -175,7 +175,22 @@
}
function draw() {
//log_debug("draw()");
log_debug("draw()");
g.setFontAlign(0,0);
g.setColor(1,1,1);
g.setFont("Vector", 24);
if (Bangle.isCompassOn === undefined) {
g.drawString("E-FW", 120, 120);
return
}
if (CALIBDATA === undefined || CALIBDATA === null) {
g.drawString("E-CALIB", 120, 120);
return
}
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
heading = newHeading(d,heading);
// sets bearing to waypoint bearing if GPS on else sets to 0 (north)

View File

@ -11,11 +11,12 @@
const INFO_NONE = 0;
const INFO_BATT = 1;
const INFO_MEM = 2;
const INFO_FW = 3;
const Y_TIME = 30;
const Y_ACTIVITY = 116;
const Y_MODELINE = 200;
function init(gps,sw) {
function init(gps,sw,hrm) {
showMem("digi init 1");
days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday","Friday", "Saturday"];
prevInfo = "";
@ -98,6 +99,9 @@
infoMode = INFO_MEM
break;
case INFO_MEM:
infoMode = INFO_FW
break;
case INFO_FW:
default:
infoMode = INFO_NONE;
break;
@ -111,17 +115,21 @@
let col = 0x07FF; // cyan
switch(infoMode) {
case INFO_NONE:
col = 0x0000;
str = "";
break;
case INFO_MEM:
val = process.memory();
str = "Memory: " + Math.round(val.usage*100/val.total) + "%";
break;
case INFO_BATT:
default:
str = "Battery: " + E.getBattery() + "%";
break;
case INFO_FW:
str = "Fw: " + process.env.VERSION;
break;
case INFO_NONE:
default:
col = 0x0000;
str = "";
break;
}
// check if we need to draw, avoid flicker

View File

@ -20,7 +20,7 @@
//console.log(o);
}
function init(gps, sw) {
function init(gps, sw, hrm) {
log_debug("gps init");
//log_debug(gps);
gpsObject = gps;
@ -79,6 +79,12 @@
g.setColor(0xFFC0);
g.setFontAlign(0, -1);
if (Bangle.isGPSOn === undefined) {
g.setColor(1,1,1);
g.drawString("E-FW", 120, Y_ACTIVITY);
return;
}
if (gpsObject.getState() === gpsObject.GPS_OFF) {
g.drawString("GPS off", 120, Y_ACTIVITY);
return;

116
apps/kitchen/heart.kit.js Normal file
View File

@ -0,0 +1,116 @@
(() => {
function getFace(){
const Y_TIME = 30;
const Y_ACTIVITY = 116;
let prevTime;
let prevBpm;
let toggle = 1;
let redrawHrmPower = true;
let intervalRefSec;
let img;
let hrmObject;
function log_debug(o) {
//console.log(o);
}
function init(gps, sw, hrm) {
img = require("heatshrink").decompress(atob("mEwwRC/ABf/+ADBh//BQgGB//AgYDBCAQWCA4QPCDAYSC//8n4EC4AiEAAo1EBZIeDAAn8BZoKHJAYL7L64LLTa6/DAAi/CKhDjGBZBIGIwQ8IHQQ8IHQYwHBQgwFFwgwGFwgwGFwowFBQwwDFwwwEFwwwEFw4wDBRAkBERAkCERIA/AAYA="));
prevTime = "-";
prevBpm = "-";
toggle = 1;
redrawHrmPower = true;
hrmObject = hrm;
intervalRefSec;
g.clear();
}
function freeResources() {
prevTime = undefined;
img = undefined;
}
function startTimer() {
draw();
intervalRefSec = setInterval(draw, 1000);
}
function stopTimer() {
if (intervalRefSec) { intervalRefSec = clearInterval(intervalRefSec); }
}
function onButtonShort(btn) {}
function onButtonLong(btn) {
log_debug("toggleHRM");
if (btn !== 1) return;
if (!Bangle.isHRMOn) return; // old firmware
hrmObject.toggleHRMPower();
prevBpm = '-';
toggle = 1; // ensure we draw the heart first
redrawHrmPower = true;
}
function draw() {
let d = new Date();
let da = d.toString().split(" ");
let time = da[4].substr(0,5);
if (time !== prevTime) {
prevTime = time;
g.setColor(0);
g.fillRect(0, Y_TIME, 239, Y_ACTIVITY -1);
g.setColor(1,1,1);
g.setFont("Vector",80);
g.setFontAlign(0,-1);
g.drawString(time, 120, Y_TIME);
}
let bpm = hrmObject.getBpm();
if (!Bangle.isHRMOn()) {
if (!redrawHrmPower) return;
redrawHrmPower = false;
g.setColor(0);
g.drawImage(img, 12, 132, {scale:2});
g.fillRect(120,120,239,239);
g.setColor(255,0,0);
//g.setColor(0xFFC0); // yellow
g.drawImage(img, 12, 132, {scale:2});
g.setFont("Vector",40);
g.setFontAlign(0,0);
g.setColor(1,1,1);
g.drawString("OFF", 180, 180);
return;
}
// draw the heart
if (++toggle % 2 === 0) {
g.setColor(0);
g.fillRect(12, 132, 108, 228);
} else {
g.setColor(255,0,0);
//g.setColor(0xFFC0); // yellow
g.drawImage(img, 12, 132, {scale:2});
}
// draw the bpm
if (bpm !== prevBpm) {
prevBpm = bpm;
g.setColor(0);
g.fillRect(120, 120, 239, 239);
g.setColor(1,1,1);
//g.setColor(0xFFC0); // yellow
g.setFont("Vector",52);
g.setFontAlign(0,0);
g.drawString(bpm, 180, 180);
}
}
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
}
return getFace;
})();

View File

@ -26,15 +26,17 @@ function nextFace(){
g.clear();
g.reset();
face.init(gpsObj, swObj);
face.init(gpsObj, swObj, hrmObj, tripObject);
startdraw();
}
// when you feel the buzzer you know you have done a long press
function longPressCheck() {
Bangle.buzz();
debug_log("long PressCheck() buzz");
if (pressTimer) {
clearInterval(pressTimer);
debug_log("clear pressTimer 2");
pressTimer = undefined;
}
}
@ -45,6 +47,11 @@ function buttonPressed(btn) {
nextFace();
} else {
firstPress = getTime();
if (pressTimer) {
debug_log("clear pressTimer 1");
clearInterval(pressTimer);
}
debug_log("set pressTimer 1");
pressTimer = setInterval(longPressCheck, 1500);
}
}
@ -53,6 +60,7 @@ function buttonPressed(btn) {
function buttonReleased(btn) {
var dur = getTime() - firstPress;
if (pressTimer) {
debug_log("clear pressTimer 3");
clearInterval(pressTimer);
pressTimer = undefined;
}
@ -180,7 +188,7 @@ GPS.prototype.determineGPSState = function() {
}
} else {
if (this.listenerCount > 0) {
Bangle.removeListener("GPS", this.processFix);
Bangle.removeListener("GPS", processFix);
this.listenerCount--;
this.log_debug("listener removed " + this.listenerCount);
}
@ -248,6 +256,7 @@ GPS.prototype.processFix = function(fix) {
this.gpsState = this.GPS_RUNNING;
if (!this.last_fix.fix && !(require("Storage").readJSON("setting.json", 1) || {}).quiet) {
Bangle.buzz(); // buzz on first position
debug_log("GPS fix buzz");
}
this.last_fix = fix;
}
@ -651,6 +660,122 @@ function stopwatchDraw() {
}
/*****************************************************************************
Heart Rate Monitor
******************************************************************************/
function HRM() {
this.bpm = 0;
this.confidence = 0;
}
HRM.prototype.log_debug = function(o) {
//console.log(o);
}
HRM.prototype.toggleHRMPower = function() {
this.log_debug("HRM.toggleHRMPower()");
if (!Bangle.isHRMOn) return; // old firmware
if (!Bangle.isHRMOn()) {
this.log_debug("HRM.toggleHRMPower(powerOn)");
Bangle.removeListener('HRM', onHRM);
Bangle.setHRMPower(1);
Bangle.on('HRM', onHRM);
} else {
this.log_debug("HRM.toggleHRMPower(powerOff)");
Bangle.removeListener('HRM', onHRM);
Bangle.setHRMPower(0);
}
// poke the hrt widget indicator to change
if (WIDGETS.widhrt !== undefined) {
WIDGETS.widhrt.draw();
}
}
HRM.prototype.getBpm = function() {
return this.bpm;
}
HRM.prototype.getConfidence = function() {
return this.confidence;
}
HRM.prototype.onHRM = function(hrm) {
this.bpm = hrm.bpm;
this.confidence = hrm.confidence;
this.log_debug("onHRM:(bpm)" + this.bpm);
this.log_debug("onHRM:(conf) " + this.confidence);
}
let hrmObj = new HRM();
function onHRM(hrm) {
hrmObj.onHRM(hrm);
}
/*****************************************************************************
Trip Counter
******************************************************************************/
function TRIP() {
this.showTrip = false;
this.tripStart = 0;
}
TRIP.prototype.resetTrip = function(steps) {
this.tripStart = (0 + steps);
console.log("resetTrip starting=" + this.tripStart);
}
TRIP.prototype.getTrip = function(steps) {
let tripSteps = (0 + steps) - this.tripStart;
console.log("getTrip steps=" + steps);
console.log("getTrip tripStart=" + this.tripStart);
console.log("getTrip=" + tripSteps);
return tripSteps;
}
TRIP.prototype.getTripState = function() {
return this.showTrip;
}
TRIP.prototype.setTripState = function(t) {
this.showTrip = t;
}
let tripObject = new TRIP();
/*****************************************************************************
Debug Object
******************************************************************************/
/*
function DEBUG() {
this.logfile = require("Storage").open("debug.log","a");
}
DEBUG.prototype.log = function(msg) {
let timestamp = new Date().toString().split(" ")[4];
let line = timestamp + ", " + msg + "\n";
this.logfile.write(line);
}
debugObj = new DEBUG();
*/
function debug_log(m) {
//debugObj.log(m);
}
/*****************************************************************************
Start App
@ -659,6 +784,6 @@ Start App
g.clear();
Bangle.loadWidgets();
face.init(gpsObj,swObj);
face.init(gpsObj,swObj, hrmObj, tripObject);
startdraw();
setButtons();

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,44 +1,28 @@
(() => {
function getFace(){
var pal4color;
var pal4red;
var buf;
var intervalRefSec;
var trip;
var prevSteps;
function init(g,sw) {
showMem("stepo init 1");
pal4color = new Uint16Array([0x0000,0xFFFF,0x7BEF,0xAFE5],0,2); // b,w,grey,greenyellow
pal4red = new Uint16Array([0x0000,0xFFFF,0xF800,0xAFE5],0,2); // b,w,red,greenyellow
buf = Graphics.createArrayBuffer(120,120,2,{msb:true});
showMem("stepo init 2");
function init(g,sw,hrm,tr) {
trip = tr;
}
function freeResources() {
showMem("stepo free 1");
pal4color = undefined;
pal4red = undefined;
buf = undefined;
showMem("stepo free 2");
}
function showMem(msg) {
var val = process.memory();
var str = msg + " " + Math.round(val.usage*100/val.total) + "%";
//console.log(str);
trip = undefined;
prevSteps = -1;
}
function flip(x,y) {
g.drawImage({width:120,height:120,bpp:2,buffer:buf.buffer, palette:pal4color}, x, y);
buf.clear();
function onButtonShort(btn) {
trip.setTripState(!trip.getTripState());
drawStepText();
}
function flip_red(x,y) {
g.drawImage({width:120,height:120,bpp:2,buffer:buf.buffer, palette:pal4red}, x, y);
buf.clear();
function onButtonLong(btn) {
trip.resetTrip(getSteps());
trip.setTripState(true);
drawStepText();
}
function onButtonShort(btn) {}
function onButtonLong(btn) {}
function radians(a) {
return a*Math.PI/180;
@ -55,10 +39,16 @@
function drawSteps() {
var i = 0;
var cx = 60;
var cy = 60;
var cx = 60 + 60;
var cy = 60 + 115;
var r = 56;
var steps = getSteps();
if (prevSteps == steps)
return;
prevSteps = steps;
var percent = steps / 10000;
if (percent > 1) percent = 1;
@ -66,38 +56,60 @@
var startrot = 0 - 180;
var midrot = -180 - (360 * percent);
var endrot = -360 - 180;
buf.setColor(3); // green-yellow
g.setColor(0xAFE5); // greenyellow
// draw guauge
for (i = startrot; i > midrot; i -= 4) {
x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i));
buf.fillCircle(x,y,4);
g.fillCircle(x,y,4);
}
buf.setColor(2); // grey
// draw remainder of guage in grey
for (i = midrot; i > endrot; i -= 4) {
x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i));
buf.fillCircle(x,y,4);
}
// draw steps
buf.setColor(1); // white
buf.setFont("Vector", 24);
buf.setFontAlign(0,0);
buf.drawString(steps, cx, cy);
// change the remaining color to RED if battery is below 25%
if (E.getBattery() > 25)
flip(60,115);
g.setColor(0x7BEF); // grey
else
flip_red(60,115);
g.setColor(0xF800); // red
// draw remainder of guage in grey or red
for (i = midrot; i > endrot; i -= 4) {
x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i));
g.fillCircle(x,y,4);
}
}
function drawStepText() {
var cx = 60 + 60;
var cy = 60 + 115;
var r = 56;
var steps = getSteps();
/*
* if our trip count is greater than todays steps then we have
* rolled over to the next day so we should reset the trip counter
*/
if (trip.getTrip(steps) < 0)
trip.resetTrip(steps);
// show trip count or total steps today
g.setFontAlign(0,0);
g.setFont("Vector", 24);
// clear the space for the text
g.clearRect(cx - (r - 12), cy - 16, cx + (r - 12), cy + 16);
if (trip.getTripState() == true) {
g.setColor(0x7BEF); // grey
//g.setColor(1,0,0); // red
g.drawString(trip.getTrip(steps), cx, cy);
} else {
g.setColor(1,1,1); // white
g.drawString(steps, cx, cy);
}
}
function draw() {
var d = new Date();
var da = d.toString().split(" ");
@ -110,12 +122,13 @@
g.drawString(time, 120, 30, true);
drawSteps();
drawStepText();
}
function getSteps() {
if (stepsWidget() !== undefined)
return stepsWidget().getSteps();
return "-";
return "E-STEPS";
}
function stepsWidget() {
@ -132,5 +145,4 @@
}
return getFace;
})();

View File

@ -2,7 +2,7 @@
function getFace(){
let swObject = undefined;
function init(gps, sw) {
function init(gps, sw, hrm) {
swObject = sw;
g.clear();
}

View File

@ -1,5 +1,6 @@
0.01: New App!
0.02: Tweaks for app loader
0.03: Fix app icon, add change of colors
0.04: Improvements and new round check option elements
0.04: Improvements and new radio button option elements
0.05: ...
0.06: Improvements, multiple rows with radio buttons

View File

@ -1,7 +1,14 @@
# Test User Input
This very basic app, allows to *test the bangle.js input interface*, and displays the result in text or a switch on/off image.
This basic app, allows to **test the bangle.js input interface** trough every types of finger interaction.
Interactrion type is displayed in text or a switch on/off image for swipe screen.
Besides the basics, the UI also includes multiple rows with radio buttons
## Captures
(Following images can be outdated)
Launcher icon
@ -31,14 +38,14 @@ Colours, font, user input, image, load widgets
## Controls
Press left area - Prints Touch1
Press righ area - Prints Touch2
Press center area - Prints Touch3
Swipe Left - Displays Switch OFF image
Swipe Right - Displays Switch ON image
BTN1 - Prints Button1
BTN2 - Prints Button2
BTN3 - Quit to Launcher
- Press left area - Prints Touch1
- Press righ area - Prints Touch2
- Press center area - Prints Touch3
- Swipe Left - Displays Switch OFF image
- Swipe Right - Displays Switch ON image
- BTN1 - Prints Button1
- BTN2 - Prints Button2
- BTN3 - Quit to Launcher
## Support

View File

@ -1,35 +1,71 @@
/* Test bangle.js input interface */
var v_mode_debug=0; //1=yes, 0=no (to disable console msg)
if (v_mode_debug==1) console.log("Debug mode enabled");
else console.log("Debug mode disabled");
var v_model=process.env.BOARD;
if (v_mode_debug==1) console.log("device="+v_model);
var v_str_version='v0.06'; //testing purpose
var x_max_screen=g.getWidth();//240;
var y_max_screen=g.getHeight(); //240;
var y_wg_bottom=g.getHeight()-25;
var y_wg_top=25;
if (v_model=='BANGLEJS') {
var x_btn_area=215;
var x_max_usable_area=x_btn_area;//Pend! only for bangle.js
var y_btn2=124; //harcoded for bangle.js cuz it is not the half of
} else x_max_usable_area=240;
var x_mid_screen=x_max_screen/2;
var colbackg='#111111';//black
var colorange='#e56e06'; //RGB format rrggbb
var v_color_lines=0xFFFF; //White hex format
var v_color_b_area=colbackg;
var v_font1size='16';
var v_clicks='0';
console.log("*** Test input interface ***");
var v_color_b_area=colbackg; //for banner area
var v_color_text='#FB0E01';
//var v_font1size=16;
var v_font1size=11; //out of quotes
var v_font2size=18;
var v_font3size=14;
var v_clicks=0;
var v_selected_row=1; //used by round option
var v_total_rows=2;//used by round option
var array_r_option=[];
var v_y_optionrow1=80;
var v_y_optionrow2=110;
var v_y_optionrow3=140;
function ClearActiveArea(){
if (v_mode_debug==1) console.log("*** Test input interface ***");
//the biggest usable area, button area not included
function ClearActiveArea(x1,y1,x2,y2){
g.setColor(colbackg);
g.fillRect(0,32,239,239); //fill all screen except widget area
//FOR BANGLE.JS (0,y_wg_top,x_max_usable_area,y_wg_bottom);
//fill all screen except widget area
g.fillRect(x1,y1,x2,y2);
g.flip();
}
function PrintHelp(){
console.log("********************************");
console.log("Log: *** Print help in screen");
ClearActiveArea();
g.setColor(colorange);
g.setFontVector(18).drawString("To test the input, try :",25,90);
function PrintHelp(){
if (v_mode_debug==1) console.log("Log: *** Print help in screen");
ClearActiveArea(0,y_wg_top,x_max_usable_area,y_wg_bottom);
g.setColor(colorange);
/* PRINT FROM widget BOTTOM */
g.setFontVector(v_font2size).drawString("To test the UI, try:",5,y_wg_bottom-(10*v_font3size));
g.flip();
g.setColor(0,1,0); //green
g.setFontVector(v_font1size).drawString("Swipe right -->", 30, 115);
g.setFontVector(v_font1size).drawString("Swipe left <--", 30, 130);
g.setFontVector(v_font1size).drawString("Click Left area", 30, 145);
g.setFontVector(v_font1size).drawString("Click Right area", 30,160);
g.setFontVector(v_font1size).drawString("Click Middle area", 30,175);
g.setFontVector(v_font1size).drawString("Press Button1 ", 30,190);
g.setFontVector(v_font1size).drawString("Press Button2 for colors", 30,205);
g.setFontVector(v_font1size).drawString("Press Button3 to Quit", 30,220);
g.setFontVector(v_font3size);
g.drawString("Swipe right -->", 30, y_wg_bottom-(8*v_font3size));
g.drawString("Swipe left <--", 30, y_wg_bottom-(7*v_font3size));
g.drawString("Click Left area", 30, y_wg_bottom-(6*v_font3size));
g.drawString("Click Right area", 30,y_wg_bottom-(5*v_font3size));
g.drawString("Click Middle area", 30,y_wg_bottom-(4*v_font3size));
g.drawString("Press Button1", 30,y_wg_bottom-(3*v_font3size));
g.drawString("Press Button2: Colour", 30,y_wg_bottom-(2*v_font3size));
g.drawString("Press Button3: Quit", 30,y_wg_bottom-v_font3size);
g.flip();
}
@ -48,8 +84,8 @@ function ClearBannerArea(){
g.fillRect(55,32,185,78);
g.flip();
}
function DrawRoundOption(area){
//arg input area Touch1=left Touch2=right
function DrawRoundOption(x_obj1,y_obj1,x_obj2,y_obj2,i_area){
//draw a img from an Image object
var img_obj_check = {
width : 30, height : 30, bpp : 4,
@ -63,21 +99,26 @@ function ClearBannerArea(){
palette : new Uint16Array([65535,63422,9532,13789,59197,57084,34266,28220,63390,65503,61310,61277,57116,55003,61309,40604]),
buffer : E.toArrayBuffer(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMwAAAAAAAAAAADMzf//3MzMAAAAAAAAAAzPxmZkRrzMwAAAAAAAAM3mZmZmRiKczAAAAAAADP5mZmZmRiKpjMAAAAAAzeZmZmZkRiKq3MwAAAAAzGZmZmZkRiKq8MwAAAAM/mZmZmZkYiKtE8iAAAAMxmZmZmZEYiqtEUiAAAAN5mZmZmRGIiqtExyAAAAPxmZmZkRiIqrRMViAAAAPxEREREYiKq7RMViAAAAP4ERERiIiqq0TFViAAAAP4iIiIiIqqtETFViAAAAN6iIiIiqq7RExV1yAAAAM0qqqqqru0RMVd0iAAAAM/uqqru7RETFXdYiAAAAAzS7u7RERMxV3dIgAAAAAzdEREREzFVd3XIgAAAAADNkREzMVVXd1iIAAAAAAAM3VVVVVd3dciAAAAAAAAAzNtVd3d1iIgAAAAAAAAADMidmZnIiIAAAAAAAAAAAAiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
};
if (area=='Touch 1') {
g.drawImage(img_obj_check,20,80);
console.log("Draw option check left");
if (i_area=='none') {
g.drawImage(img_obj_uncheck,x_obj1,y_obj1);
g.drawImage(img_obj_uncheck,x_obj2,y_obj2);
}
else if (i_area=='Touch 1') {
g.drawImage(img_obj_check,x_obj1,y_obj1);
g.drawImage(img_obj_uncheck,x_obj2,y_obj2);
if (v_mode_debug==1) console.log("Draw option check left");
}
else g.drawImage(img_obj_uncheck,20,80);
if (area=='Touch 2') {
g.drawImage(img_obj_check,190,80);
console.log("Draw option check right");
else if (i_area=='Touch 2') {
g.drawImage(img_obj_uncheck,x_obj1,y_obj1);
g.drawImage(img_obj_check,x_obj2,y_obj2);
if (v_mode_debug==1) console.log("Draw option check right");
}
else g.drawImage(img_obj_uncheck,190,80);
}
function DrawSwitch(swipedir){
if (swipedir==' <---') {
console.log("Draw switch <--");
if (v_mode_debug==1) console.log("Draw switch <--");
var img_off = {
width : 48, height : 48, bpp : 2,
transparent : 0,
@ -87,7 +128,7 @@ if (swipedir==' <---') {
g.drawImage(img_off,99,33);
}
else if (swipedir==' --->') {
console.log("Draw switch -->");
if (v_mode_debug==1) console.log("Draw switch -->");
var img_on = {
width : 48, height : 48, bpp : 2,
transparent : 0,
@ -100,70 +141,154 @@ if (swipedir==' <---') {
function PrintUserInput(boton){
console.log("Pressed touch/BTN",boton);
if (v_clicks=='0') {
if (v_mode_debug==1) console.log("Pressed touch/BTN",boton);
if (v_clicks==0) {
PrintAreas();
v_clicks=1;
v_clicks++;
}
ClearBannerArea();
if (boton==' <---') DrawSwitch(boton);
else if (boton==' --->') DrawSwitch(boton);
else
{ //a BUTTON or AREA AND NO swipe /slide
if (boton=='Touch 1'||boton=='Touch 2') DrawRoundOption(boton);
g.setColor(colorange);
g.setFontVector(30).drawString(boton, 63, 55);
}
//all input but not swipe
else {
g.setColor(colorange);
//Call info banner
g.setFontVector(30).drawString(boton, 63, 55);
if ((boton=='Touch 1')||(boton=='Touch 2')){
if (v_selected_row==1) v_y_opt=v_y_optionrow1;
else if (v_selected_row==2) v_y_opt=v_y_optionrow2;
DrawRoundOption(20,v_y_opt,190,v_y_opt,boton);
//set the option value in an array
array_r_option[v_selected_row]=boton;
if (v_mode_debug==1) console.log("array["+v_selected_row+"]="+array_r_option[v_selected_row]);
}
}
g.flip();
}
function PrintBtn1(boton){
console.log("Pressed BTN1");
if (v_clicks=='0'){
PrintAreas();
v_clicks=1;
function Btn1Clkd(boton){
if (v_mode_debug==1) console.log("Pressed BTN1");
if (v_clicks==0){
PrintAreas(); //only 1st time
//v_selected_row=1;
v_clicks++;
}
else if ((v_clicks>0)&&(v_selected_row!=v_total_rows)){
v_selected_row++;
//Params: row_arrow, row_clear_area
if (v_mode_debug==1) console.log("row :"+v_selected_row);
DrawRowSelArrow(v_selected_row,v_selected_row-1);
v_clicks++;
}
else if ((v_clicks>0)&&(v_selected_row==v_total_rows)){
DrawRowSelArrow(1,v_selected_row);
if (v_mode_debug==1) console.log("last row :"+v_selected_row);
v_selected_row=1;
v_clicks++;
}
PrintUserInput("Button1");
}
function PrintBtn2(boton){
console.log("Pressed BTN2");
function Btn2Clkd(boton){
if (v_mode_debug==1) console.log("Pressed BTN2");
v_color_b_area=ChangeColorBannerArea(v_color_b_area);
if (v_clicks=='0'){
PrintAreas();
v_clicks=1;
if (v_clicks==0){
PrintAreas();//only 1st time
v_clicks++;
}
PrintUserInput("Button2");
}
function DrawBangleButtons(){
/*Button name */
g.setColor(v_color_text); //green
g.setFontVector(v_font3size);
g.drawString("B1", x_max_screen-g.stringWidth("B1"),y_wg_top);
g.drawString("B2", x_max_screen-g.stringWidth("B2"),y_btn2);
//y y_wg_bottom-v_font3size ?
g.drawString("B3",x_max_screen-g.stringWidth("B3"),y_wg_bottom);
/*Button area description */
g.setFontVector(v_font1size);
g.setColor(v_color_lines);
//y_wg_bottom-(2*v_font1size)
g.drawString("Quit", x_max_screen-g.stringWidth("Quit"),y_wg_bottom-v_font1size-2);
//Print version
if (v_mode_debug==1){
g.setColor(0,1,0); //green
//y_wg_bottom-(2*v_font1size)
g.drawString(v_str_version, x_max_screen-g.stringWidth(v_str_version),y_wg_bottom-(v_font1size*3));
}
//under btn2, left top 90grades
g.setFontAlign(-1,-1,1);
g.drawString("Color", x_max_screen-v_font1size,y_btn2+v_font3size);
//g.drawString("Color", x_max_screen-g.stringWidth("Color"),y_btn2+v_font1size);
g.setColor(0,1,0); //green
g.drawString("Up", x_max_screen-v_font1size,y_wg_top+v_font3size);
g.setColor(v_color_lines);
g.drawString("Down", x_max_screen-2*v_font1size,y_wg_top+v_font3size);
g.flip();
//back to standard /horizontal
g.setFontAlign(-1,-1,0);
}
function PrintAreas(){
console.log("********************************");
console.log("Log: *** Print Areas in screen");
ClearActiveArea();
g.setColor(v_color_lines);
g.drawLine(1, 140, 1, 200);//vline left border
g.drawLine(239, 140, 239, 200);//vlide right border
g.drawLine(120, 100, 120, 135);//vline middle separation top
g.drawLine(120, 170, 120, 200);//vline middle separation bottom
function DrawRowSelArrow(v_drawRow, v_clearRow){
//Params: row_arrow, row_clear_area
//for clear previous draw arrow
if (v_clearRow!== undefined) {
g.setColor(colbackg);
if (v_clearRow==1) v_y_arrow=v_y_optionrow1+14;
else if (v_clearRow==2) v_y_arrow=v_y_optionrow2+14;
else if (v_clearRow==3) v_y_arrow=v_y_optionrow3+14;
g.fillRect(5,v_y_arrow-5,13,v_y_arrow+5);
g.flip();
}
//draw an arrow to select a row
if (v_drawRow!== undefined) {
if (v_drawRow==1) v_y_arrow=v_y_optionrow1+14;
else if (v_drawRow==2) v_y_arrow=v_y_optionrow2+14;
else if (v_drawRow==3) v_y_arrow=v_y_optionrow3+14;
g.setColor(v_color_lines);
g.drawLine(5, v_y_arrow, 13, v_y_arrow);//horizontal
g.drawLine(13, v_y_arrow, 10, v_y_arrow-5);//over diag
g.drawLine(13, v_y_arrow, 10, v_y_arrow+5);//under diag
g.flip();
}
else console.log("Error: Param row nbr missing");
}
function PrintAreas(){
if (v_mode_debug==1) console.log("Log: *** Print Areas in screen");
ClearActiveArea(0,y_wg_top,x_max_usable_area,y_wg_bottom);
g.setColor(v_color_lines);
//BTN1
g.setFontVector(v_font1size).drawString("Color<-", 130,125);
//BTN13
g.setFontVector(v_font1size).drawString("Quit<-", 135,225);
g.flip();
g.setColor(0,1,0); //green
g.setFontVector(v_font1size).drawString("BTN1", 195,45);
DrawRowSelArrow(1);
DrawRoundOption(20,v_y_optionrow1,190,v_y_optionrow1,'none');
DrawRoundOption(20,v_y_optionrow2,190,v_y_optionrow2,'none');
g.setFontVector(v_font1size).drawString("BTN2", 195,125);
g.drawLine(x_max_screen-1, 50, x_max_screen-1, 65);//vlide right border
g.drawLine(x_mid_screen, 80, x_mid_screen, 105);//vline middle separation part1 up
g.drawLine(x_mid_screen, 140, x_mid_screen, 180);//vline middle separation part2 down
g.setFontVector(v_font1size).drawString("BTN3", 195,225);
g.setFontVector(v_font1size).drawString("Middle area", 80,155);
g.setFontVector(v_font1size).drawString("Left area", 15, 185);
g.setFontVector(v_font1size).drawString("Right area", 140,185);
g.flip();
g.setFontVector(v_font3size);
g.drawString("Middle area", 80,155);
g.drawString("Left area", 15, 185);
g.drawString("Right area", 140,185);
if (v_model=='BANGLEJS') DrawBangleButtons();
}
function UserInput(){
@ -181,23 +306,26 @@ function UserInput(){
}
});
//only the name of the function
setWatch(PrintBtn1, BTN1, { repeat: true });
setWatch(PrintBtn2, BTN2, { repeat: true });
setWatch(Btn1Clkd, BTN1, { repeat: true });
setWatch(Btn2Clkd, BTN2, { repeat: true });
setWatch(Bangle.showLauncher, BTN3, { repeat: true });
Bangle.on('swipe', dir => {
if(dir == 1) PrintUserInput(" --->");
else PrintUserInput(" <---");
});
console.log("Log: Input conditions loaded");
if (v_mode_debug==1) console.log("Log: Input conditions loaded");
} //end of UserInput
//Main code
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
//optional line below widgets area
g.setColor(v_color_lines);
g.drawLine(60, 30, 180, 30);
g.flip();
//optional line below widgets area
//g.drawLine(60, 30, 180, 30);
//g.flip();
//end optional
PrintHelp();
PrintHelp();
UserInput();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 112 KiB