Merge remote-tracking branch 'origin/master'

pull/2178/head
Adrian Kirk 2022-10-15 23:19:39 +01:00
commit 66f77f5aee
No known key found for this signature in database
GPG Key ID: 5A448EB0FC623526
29 changed files with 350 additions and 117 deletions

View File

@ -2,3 +2,4 @@
0.02: Use the new multiplatform 'Layout' library 0.02: Use the new multiplatform 'Layout' library
0.03: Exit as first menu option, dont show decimal places for seconds 0.03: Exit as first menu option, dont show decimal places for seconds
0.04: Localisation, change Exit->Back to allow back-arrow to appear on 2v13 firmware 0.04: Localisation, change Exit->Back to allow back-arrow to appear on 2v13 firmware
0.05: Add max G values during recording, record actual G values and magnitude to CSV

View File

@ -1,5 +1,6 @@
var fileNumber = 0; var fileNumber = 0;
var MAXLOGS = 9; var MAXLOGS = 9;
var logRawData = false;
function getFileName(n) { function getFileName(n) {
return "accellog."+n+".csv"; return "accellog."+n+".csv";
@ -24,6 +25,11 @@ function showMenu() {
/*LANG*/"View Logs" : function() { /*LANG*/"View Logs" : function() {
viewLogs(); viewLogs();
}, },
/*LANG*/"Log raw data" : {
value : logRawData,
format : v => v?/*LANG*/"Yes":/*LANG*/"No",
onchange : v => { logRawData=v; }
},
}; };
E.showMenu(menu); E.showMenu(menu);
} }
@ -78,6 +84,7 @@ function viewLogs() {
} }
function startRecord(force) { function startRecord(force) {
var stopped = false;
if (!force) { if (!force) {
// check for existing file // check for existing file
var f = require("Storage").open(getFileName(fileNumber), "r"); var f = require("Storage").open(getFileName(fileNumber), "r");
@ -92,39 +99,101 @@ function startRecord(force) {
var Layout = require("Layout"); var Layout = require("Layout");
var layout = new Layout({ type: "v", c: [ var layout = new Layout({ type: "v", c: [
{ type: "h", c: [
{ type: "v", c: [
{type:"txt", font:"6x8", label:/*LANG*/"Samples", pad:2}, {type:"txt", font:"6x8", label:/*LANG*/"Samples", pad:2},
{type:"txt", id:"samples", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg}, {type:"txt", id:"samples", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg},
]},
{ type: "v", c: [
{type:"txt", font:"6x8", label:/*LANG*/"Time", pad:2}, {type:"txt", font:"6x8", label:/*LANG*/"Time", pad:2},
{type:"txt", id:"time", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg}, {type:"txt", id:"time", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg},
{type:"txt", font:"6x8:2", label:/*LANG*/"RECORDING", bgCol:"#f00", pad:5, fillx:1}, ]},
] ]},
},{btns:[ // Buttons... { type: "h", c: [
{label:/*LANG*/"STOP", cb:()=>{ { type: "v", c: [
Bangle.removeListener('accel', accelHandler); {type:"txt", font:"6x8", label:/*LANG*/"Max X", pad:2},
{type:"txt", id:"maxX", font:"6x8", label:" - ", pad:5, bgCol:g.theme.bg},
]},
{ type: "v", c: [
{type:"txt", font:"6x8", label:/*LANG*/"Max Y", pad:2},
{type:"txt", id:"maxY", font:"6x8", label:" - ", pad:5, bgCol:g.theme.bg},
]},
{ type: "v", c: [
{type:"txt", font:"6x8", label:/*LANG*/"Max Z", pad:2},
{type:"txt", id:"maxZ", font:"6x8", label:" - ", pad:5, bgCol:g.theme.bg},
]},
]},
{type:"txt", font:"6x8", label:/*LANG*/"Max G", pad:2},
{type:"txt", id:"maxMag", font:"6x8:4", label:" - ", pad:5, bgCol:g.theme.bg},
{type:"txt", id:"state", font:"6x8:2", label:/*LANG*/"RECORDING", bgCol:"#f00", pad:5, fillx:1},
]},
{
btns:[ // Buttons...
{id: "btnStop", label:/*LANG*/"STOP", cb:()=>{
if (stopped) {
showMenu(); showMenu();
}
else {
Bangle.removeListener('accel', accelHandler);
layout.state.label = /*LANG*/"STOPPED";
layout.state.bgCol = /*LANG*/"#0f0";
stopped = true;
layout.render();
}
}} }}
]}); ]});
layout.render(); layout.render();
// now start writing // now start writing
var f = require("Storage").open(getFileName(fileNumber), "w"); var f = require("Storage").open(getFileName(fileNumber), "w");
f.write("Time (ms),X,Y,Z\n"); f.write("Time (ms),X,Y,Z,Total\n");
var start = getTime(); var start = getTime();
var sampleCount = 0; var sampleCount = 0;
var maxMag = 0;
var maxX = 0;
var maxY = 0;
var maxZ = 0;
function accelHandler(accel) { function accelHandler(accel) {
var t = getTime()-start; var t = getTime()-start;
if (logRawData) {
f.write([ f.write([
t*1000, t*1000,
accel.x*8192, accel.x*8192,
accel.y*8192, accel.y*8192,
accel.z*8192].map(n=>Math.round(n)).join(",")+"\n"); accel.z*8192,
accel.mag*8192,
].map(n=>Math.round(n)).join(",")+"\n");
} else {
f.write([
Math.round(t*1000),
accel.x,
accel.y,
accel.z,
accel.mag,
].join(",")+"\n");
}
if (accel.mag > maxMag) {
maxMag = accel.mag.toFixed(2);
}
if (accel.x > maxX) {
maxX = accel.x.toFixed(2);
}
if (accel.y > maxY) {
maxY = accel.y.toFixed(2);
}
if (accel.z > maxZ) {
maxZ = accel.z.toFixed(2);
}
sampleCount++; sampleCount++;
layout.samples.label = sampleCount; layout.samples.label = sampleCount;
layout.time.label = Math.round(t)+"s"; layout.time.label = Math.round(t)+"s";
layout.render(layout.samples); layout.maxX.label = maxX;
layout.render(layout.time); layout.maxY.label = maxY;
layout.maxZ.label = maxZ;
layout.maxMag.label = maxMag;
layout.render();
} }
Bangle.setPollInterval(80); // 12.5 Hz - the default Bangle.setPollInterval(80); // 12.5 Hz - the default

View File

@ -2,7 +2,7 @@
"id": "accellog", "id": "accellog",
"name": "Acceleration Logger", "name": "Acceleration Logger",
"shortName": "Accel Log", "shortName": "Accel Log",
"version": "0.04", "version": "0.05",
"description": "Logs XYZ acceleration data to a CSV file that can be downloaded to your PC", "description": "Logs XYZ acceleration data to a CSV file that can be downloaded to your PC",
"icon": "app.png", "icon": "app.png",
"tags": "outdoor", "tags": "outdoor",

View File

@ -10,11 +10,21 @@ Basic agenda reading the events synchronised from GadgetBridge.
* Show the colour of the calendar in the list * Show the colour of the calendar in the list
* Display description, location and calendar name after tapping on events * Display description, location and calendar name after tapping on events
### Troubleshooting
For the events sync to work, GadgetBridge needs to have the calendar permission and calendar sync should be enabled in the devices settings (gear sign in GB, also check the blacklisted calendars there, if events are missing).
Keep in mind that GadgetBridge won't synchronize all events on your calendar, just the ones in a time window of 7 days (you don't want your watch to explode), ideally every day old events get deleted since they appear out of such window.
#### Force Sync
If for any reason events still cannot sync or some are missing, you can try any of the following (just one, you normally don't need to do this):
1. from GB open the burger menu (side), tap debug and set time.
2. from the bangle, open settings > apps > agenda > Force calendar sync, then select not to delete the local events (this is equivalent to option 1).
3. do like option 2 but delete events, GB will synchronize a fresh database instead of patching the old one (good in case you somehow cannot get rid of older events)
After any of the options, you may need to disconnect/force close Gadgetbridge before reconnecting and let it sync (give it some time for that too), restart the agenda app on the bangle after a while to see the changes.
### Report a bug ### Report a bug
You can easily open an issue in the espruino repo, but I won't be notified and it might take time. You can easily open an issue in the espruino repo, but I won't be notified and it might take time.
If you want a (hopefully) quicker response, just report [on my fork](https://github.com/glemco/BangleApps). If you want a (hopefully) quicker response, just report [on my fork](https://github.com/glemco/BangleApps).
### Known Problems
Any all-day event lasts just one day: that is a GB limitation that we will likely fix in the future.

View File

@ -5,3 +5,4 @@
0.05: Use locale for speed and distance + fix Vector font sizes 0.05: Use locale for speed and distance + fix Vector font sizes
0.06: Move waypoints.json (and editor) to 'waypoints' app 0.06: Move waypoints.json (and editor) to 'waypoints' app
0.07: Add support for b2 0.07: Add support for b2
0.08: Fix not displaying of wpindex = 0, correct compass drawing and nm calculation on b2

View File

@ -36,7 +36,7 @@ function drawCompass(course) {
} }
xpos+=15; xpos+=15;
} }
if (wpindex!=0) { if (wpindex>=0) {
var bpos = brg - course; var bpos = brg - course;
if (bpos>180) bpos -=360; if (bpos>180) bpos -=360;
if (bpos<-180) bpos +=360; if (bpos<-180) bpos +=360;
@ -220,7 +220,7 @@ function nextwp(inc){
} }
function doselect(){ function doselect(){
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) { if (selected && wpindex>=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon}; waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex]; wp = waypoints[wpindex];
require("waypoints").save(waypoints); require("waypoints").save(waypoints);

View File

@ -13,7 +13,7 @@ var loc = {
distance: [ distance: [
require("locale").distance, require("locale").distance,
(m) => { (m) => {
return (m / 1.852).toFixed(3) + "nm "; return (m / 1852).toFixed(3) + "nm ";
} }
] ]
}; };
@ -21,7 +21,7 @@ var loc = {
function drawCompass(course) { function drawCompass(course) {
if (!candraw) return; if (!candraw) return;
g.setColor(g.theme.fg); g.reset().clearRect(0, 24, 175, 71);
g.setFont("Vector", 18); g.setFont("Vector", 18);
var start = course - 90; var start = course - 90;
if (start < 0) start += 360; if (start < 0) start += 360;
@ -43,7 +43,7 @@ function drawCompass(course) {
} }
xpos += 12; xpos += 12;
} }
if (wpindex != 0) { if (wpindex >= 0) {
var bpos = brg - course; var bpos = brg - course;
if (bpos > 180) bpos -= 360; if (bpos > 180) bpos -= 360;
if (bpos < -180) bpos += 360; if (bpos < -180) bpos += 360;
@ -106,9 +106,8 @@ function distance(a, b) {
var selected = false; var selected = false;
function drawN() { function drawN() {
g.clearRect(0, 89, 175, 175); g.reset().clearRect(0, 89, 175, 175);
var txt = loc.speed[locindex](speed); var txt = loc.speed[locindex](speed);
g.setColor(g.theme.fg);
g.setFont("6x8", 2); g.setFont("6x8", 2);
g.drawString("o", 68, 87); g.drawString("o", 68, 87);
g.setFont("6x8", 1); g.setFont("6x8", 1);
@ -117,10 +116,8 @@ function drawN() {
var cs = course.toString().padStart(3, "0"); var cs = course.toString().padStart(3, "0");
g.drawString(cs, 2, 89); g.drawString(cs, 2, 89);
g.drawString(txt.substring(0, txt.length - 3), 92, 89); g.drawString(txt.substring(0, txt.length - 3), 92, 89);
g.setColor(g.theme.fg);
g.setFont("Vector", 18); g.setFont("Vector", 18);
var bs = brg.toString().padStart(3, "0"); var bs = brg.toString().padStart(3, "0");
g.setColor(g.theme.fg);
g.drawString("Brg:", 1, 128); g.drawString("Brg:", 1, 128);
g.drawString("Dist:", 1, 148); g.drawString("Dist:", 1, 148);
g.setColor(selected ? g.theme.bgH : g.theme.bg); g.setColor(selected ? g.theme.bgH : g.theme.bg);
@ -241,7 +238,7 @@ function nextwp(inc) {
} }
function doselect() { function doselect() {
if (selected && wpindex != 0 && waypoints[wpindex].lat === undefined && savedfix.fix) { if (selected && wpindex >= 0 && waypoints[wpindex].lat === undefined && savedfix.fix) {
waypoints[wpindex] = { waypoints[wpindex] = {
name: "@" + wp.name, name: "@" + wp.name,
lat: savedfix.lat, lat: savedfix.lat,

View File

@ -1,7 +1,7 @@
{ {
"id": "gpsnav", "id": "gpsnav",
"name": "GPS Navigation", "name": "GPS Navigation",
"version": "0.07", "version": "0.08",
"description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor", "description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor",
"screenshots": [{"url":"screenshot-b2.png"}], "screenshots": [{"url":"screenshot-b2.png"}],
"icon": "icon.png", "icon": "icon.png",

View File

@ -1 +1,2 @@
0.01: New App! 0.01: New App!
0.02: Make selection of background activity more explicit

View File

@ -39,5 +39,9 @@ If the compass fallback starts to show unreliable values, you can reset the cali
## Widget ## Widget
The widget keeps the sensors alive and records some very basic statics when the app is not started. The widget keeps the sensors alive and records some very basic statistics when the app is not started. It shows as the app icon in the widget bar when the background task is active.
This uses a lot of power so ensure to stop the app if you are not actively using it. This uses a lot of power so ensure to stop the app if you are not actively using it.
# Creator
[halemmerich](https://github.com/halemmerich)

View File

@ -6,8 +6,8 @@ if (showWidgets){
Bangle.loadWidgets(); Bangle.loadWidgets();
} }
let state = WIDGETS["gpstrek"].getState(); let state = WIDGETS.gpstrek.getState();
WIDGETS["gpstrek"].start(); WIDGETS.gpstrek.start(false);
function parseNumber(toParse){ function parseNumber(toParse){
if (toParse.includes(".")) return parseFloat(toParse); if (toParse.includes(".")) return parseFloat(toParse);
@ -582,6 +582,18 @@ function showWaypointMenu(){
E.showMenu(menu); E.showMenu(menu);
} }
function showBackgroundMenu(){
let menu = {
"" : {
"title" : "Background",
back : showMenu,
},
"Start" : ()=>{ E.showPrompt("Start?").then((v)=>{ if (v) {WIDGETS.gpstrek.start(true); removeMenu();} else {E.showMenu(mainmenu);}});},
"Stop" : ()=>{ E.showPrompt("Stop?").then((v)=>{ if (v) {WIDGETS.gpstrek.stop(true); removeMenu();} else {E.showMenu(mainmenu);}});},
};
E.showMenu(menu);
}
function showMenu(){ function showMenu(){
var mainmenu = { var mainmenu = {
"" : { "" : {
@ -590,10 +602,9 @@ function showMenu(){
}, },
"Route" : showRouteMenu, "Route" : showRouteMenu,
"Waypoint" : showWaypointMenu, "Waypoint" : showWaypointMenu,
"Background" : showBackgroundMenu,
"Calibration": showCalibrationMenu, "Calibration": showCalibrationMenu,
"Start" : ()=>{ E.showPrompt("Start?").then((v)=>{ if (v) {state.active = true; removeMenu();} else {E.showMenu(mainmenu);}});}, "Reset" : ()=>{ E.showPrompt("Do Reset?").then((v)=>{ if (v) {WIDGETS.gpstrek.resetState(); removeMenu();} else {E.showMenu(mainmenu);}});},
"Stop" : ()=>{ E.showPrompt("Stop?").then((v)=>{ if (v) {WIDGETS["gpstrek"].stop(); removeMenu();} else {E.showMenu(mainmenu);}});},
"Reset" : ()=>{ E.showPrompt("Do Reset?").then((v)=>{ if (v) {WIDGETS["gpstrek"].resetState(); removeMenu();} else {E.showMenu(mainmenu);}});},
"Slices" : { "Slices" : {
value : numberOfSlices, value : numberOfSlices,
min:1,max:6,step:1, min:1,max:6,step:1,

View File

@ -1,7 +1,7 @@
{ {
"id": "gpstrek", "id": "gpstrek",
"name": "GPS Trekking", "name": "GPS Trekking",
"version": "0.01", "version": "0.02",
"description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!", "description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!",
"icon": "icon.png", "icon": "icon.png",
"screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}], "screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}],
@ -13,5 +13,6 @@
{"name":"gpstrek.app.js","url":"app.js"}, {"name":"gpstrek.app.js","url":"app.js"},
{"name":"gpstrek.wid.js","url":"widget.js"}, {"name":"gpstrek.wid.js","url":"widget.js"},
{"name":"gpstrek.img","url":"app-icon.js","evaluate":true} {"name":"gpstrek.img","url":"app-icon.js","evaluate":true}
] ],
"data": [{"name":"gpstrek.state.json"}]
} }

View File

@ -1,6 +1,7 @@
(() => { (() => {
const STORAGE=require('Storage'); const STORAGE=require('Storage');
let state = STORAGE.readJSON("gpstrek.state.json")||{}; let state = STORAGE.readJSON("gpstrek.state.json")||{};
let bgChanged = false;
function saveState(){ function saveState(){
state.saved = Date.now(); state.saved = Date.now();
@ -8,7 +9,7 @@ function saveState(){
} }
E.on("kill",()=>{ E.on("kill",()=>{
if (state.active){ if (bgChanged){
saveState(); saveState();
} }
}); });
@ -72,7 +73,7 @@ function onPressure(e) {
} }
} }
function start(){ function start(bg){
Bangle.on('GPS', onGPS); Bangle.on('GPS', onGPS);
Bangle.on("HRM", onPulse); Bangle.on("HRM", onPulse);
Bangle.on("mag", onMag); Bangle.on("mag", onMag);
@ -83,13 +84,20 @@ function start(){
Bangle.setHRMPower(1, "gpstrek"); Bangle.setHRMPower(1, "gpstrek");
Bangle.setCompassPower(1, "gpstrek"); Bangle.setCompassPower(1, "gpstrek");
Bangle.setBarometerPower(1, "gpstrek"); Bangle.setBarometerPower(1, "gpstrek");
if (bg){
if (!state.active) bgChanged = true;
state.active = true; state.active = true;
saveState();
}
Bangle.drawWidgets(); Bangle.drawWidgets();
} }
function stop(){ function stop(bg){
if (bg){
if (state.active) bgChanged = true;
state.active = false; state.active = false;
saveState(); saveState();
}
Bangle.drawWidgets(); Bangle.drawWidgets();
} }
@ -107,7 +115,7 @@ if (state.saved && state.saved < Date.now() - 60000){
} }
if (state.active){ if (state.active){
start(); start(false);
} }
WIDGETS["gpstrek"]={ WIDGETS["gpstrek"]={

View File

@ -2,3 +2,4 @@
0.02: implemented "direct launch" and "one click exit" settings 0.02: implemented "direct launch" and "one click exit" settings
0.03: Use default Bangle formatter for booleans 0.03: Use default Bangle formatter for booleans
0.04: Support new fast app switching 0.04: Support new fast app switching
0.05: Allow to directly eval apps instead of loading

View File

@ -10,3 +10,7 @@ This launcher shows 9 apps per screen, making it much faster to navigate versus
## Technical note ## Technical note
The app uses `E.showScroller`'s code in the app but not the function itself because `E.showScroller` doesn't report the position of a press to the select function. The app uses `E.showScroller`'s code in the app but not the function itself because `E.showScroller` doesn't report the position of a press to the select function.
### Fastload option
Fastload clears up the memory used by the launcher and directly evals the code of the app to load. This means if widgets are loaded (fullscreen option) it is possible that widgets stay loaded in apps not expecting that and the widgets may draw over the app.

View File

@ -1,20 +1,10 @@
{ {
const s = require("Storage"); const s = require("Storage");
const settings = s.readJSON("launch.json", true) || { showClocks: true, fullscreen: false,direct:false,oneClickExit:false }; const settings = s.readJSON("launch.json", true) || { showClocks: true, fullscreen: false,direct:false,oneClickExit:false };
function returnToClock() {
Bangle.setUI();
setTimeout(eval,0,s.read(".bootcde"));
}
if( settings.oneClickExit)
setWatch(returnToClock, BTN1);
if (!settings.fullscreen) { if (!settings.fullscreen) {
if (!global.WIDGETS) Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
} }
var apps = s var apps = s
.list(/\.info$/) .list(/\.info$/)
.map((app) => { .map((app) => {
@ -38,34 +28,29 @@
); );
apps.sort((a, b) => { apps.sort((a, b) => {
var n = (0 | a.sortorder) - (0 | b.sortorder); var n = (0 | a.sortorder) - (0 | b.sortorder);
if (n) return n; // do sortorder first if (n) return n;
if (a.name < b.name) return -1; if (a.name < b.name) return -1;
if (a.name > b.name) return 1; if (a.name > b.name) return 1;
return 0; return 0;
}); });
apps.forEach((app) => { apps.forEach((app) => {
if (app.icon) app.icon = s.read(app.icon); // should just be a link to a memory area if (app.icon) app.icon = s.read(app.icon);
}); });
let scroll = 0; let scroll = 0;
let selectedItem = -1; let selectedItem = -1;
const R = Bangle.appRect; const R = Bangle.appRect;
const iconSize = 48; const iconSize = 48;
const appsN = Math.floor(R.w / iconSize); const appsN = Math.floor(R.w / iconSize);
const whitespace = (R.w - appsN * iconSize) / (appsN + 1); const whitespace = (R.w - appsN * iconSize) / (appsN + 1);
const itemSize = iconSize + whitespace; const itemSize = iconSize + whitespace;
let drawItem = function(itemI, r) {
function drawItem(itemI, r) {
g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1); g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1);
let x = 0; let x = 0;
for (let i = itemI * appsN; i < appsN * (itemI + 1); i++) { for (let i = itemI * appsN; i < appsN * (itemI + 1); i++) {
if (!apps[i]) break; if (!apps[i]) break;
x += whitespace; x += whitespace;
if (!apps[i].icon) { if (!apps[i].icon) {
g.setFontAlign(0,0,0).setFont("12x20:2").drawString("?", x + r.x+iconSize/2, r.y + iconSize/2); g.setFontAlign(0, 0, 0).setFont("12x20:2").drawString("?", x + r.x + iconSize / 2, r.y + iconSize / 2);
} else { } else {
g.drawImage(apps[i].icon, x + r.x, r.y); g.drawImage(apps[i].icon, x + r.x, r.y);
} }
@ -80,9 +65,8 @@
x += iconSize; x += iconSize;
} }
drawText(itemI); drawText(itemI);
} };
let drawItemAuto = function(i) {
function drawItemAuto(i) {
var y = idxToY(i); var y = idxToY(i);
g.reset().setClipRect(R.x, y, R.x2, y + itemSize); g.reset().setClipRect(R.x, y, R.x2, y + itemSize);
drawItem(i, { drawItem(i, {
@ -92,11 +76,9 @@
h: itemSize h: itemSize
}); });
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1); g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
} };
let lastIsDown = false; let lastIsDown = false;
let drawText = function(i) {
function drawText(i) {
const selectedApp = apps[selectedItem]; const selectedApp = apps[selectedItem];
const idy = (selectedItem - (selectedItem % 3)) / 3; const idy = (selectedItem - (selectedItem % 3)) / 3;
if (!selectedApp || i != idy) return; if (!selectedApp || i != idy) return;
@ -111,14 +93,13 @@
appY + rect.height / 2 appY + rect.height / 2
); );
g.drawString(selectedApp.name, R.w / 2, appY); g.drawString(selectedApp.name, R.w / 2, appY);
} };
let selectItem = function(id, e) {
function selectItem(id, e) {
const iconN = E.clip(Math.floor((e.x - R.x) / itemSize), 0, appsN - 1); const iconN = E.clip(Math.floor((e.x - R.x) / itemSize), 0, appsN - 1);
const appId = id * appsN + iconN; const appId = id * appsN + iconN;
if( settings.direct && apps[appId]) if( settings.direct && apps[appId])
{ {
load(apps[appId].src); loadApp(apps[appId].src);
return; return;
} }
if (appId == selectedItem && apps[appId]) { if (appId == selectedItem && apps[appId]) {
@ -126,22 +107,19 @@
if (!app.src || s.read(app.src) === undefined) { if (!app.src || s.read(app.src) === undefined) {
E.showMessage( /*LANG*/ "App Source\nNot found"); E.showMessage( /*LANG*/ "App Source\nNot found");
} else { } else {
load(app.src); loadApp(app.src);
} }
} }
selectedItem = appId; selectedItem = appId;
drawItems(); drawItems();
} };
let idxToY = function(i) {
function idxToY(i) {
return i * itemSize + R.y - (scroll & ~1); return i * itemSize + R.y - (scroll & ~1);
} };
let YtoIdx = function(y) {
function YtoIdx(y) {
return Math.floor((y + (scroll & ~1) - R.y) / itemSize); return Math.floor((y + (scroll & ~1) - R.y) / itemSize);
} };
let drawItems = function() {
function drawItems() {
g.reset().clearRect(R.x, R.y, R.x2, R.y2); g.reset().clearRect(R.x, R.y, R.x2, R.y2);
g.setClipRect(R.x, R.y, R.x2, R.y2); g.setClipRect(R.x, R.y, R.x2, R.y2);
var a = YtoIdx(R.y); var a = YtoIdx(R.y);
@ -154,14 +132,11 @@
h: itemSize, h: itemSize,
}); });
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1); g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
} };
drawItems(); drawItems();
g.flip(); g.flip();
const itemsN = Math.ceil(apps.length / appsN); const itemsN = Math.ceil(apps.length / appsN);
let onDrag = function(e) {
function onDrag(e){
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
g.setBgColor(g.theme.bg); g.setBgColor(g.theme.bg);
let dy = e.dy; let dy = e.dy;
@ -190,7 +165,6 @@
y += itemSize; y += itemSize;
} }
} else { } else {
// d>0
g.setClipRect(R.x, R.y, R.x2, R.y + dy); g.setClipRect(R.x, R.y, R.x2, R.y + dy);
let i = YtoIdx(R.y + dy); let i = YtoIdx(R.y + dy);
let y = idxToY(i); let y = idxToY(i);
@ -206,8 +180,7 @@
} }
} }
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1); g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
} };
Bangle.setUI({ Bangle.setUI({
mode: "custom", mode: "custom",
drag: onDrag, drag: onDrag,
@ -217,4 +190,36 @@
selectItem(i, e); selectItem(i, e);
}, },
}); });
const returnToClock = function() {
loadApp(".bootcde");
};
let watch;
let loadApp;
if (settings.fastload){
loadApp = function(name) {
Bangle.setUI();
if (watch) clearWatch(watch);
apps = [];
delete drawItemAuto;
delete drawText;
delete selectItem;
delete onDrag;
delete drawItems;
delete drawItem;
delete returnToClock;
delete idxToY;
delete YtoIdx;
delete settings;
setTimeout(eval, 0, s.read(name));
return;
};
} else {
loadApp = function(name) {
load(name);
}
}
if (settings.oneClickExit) {
watch = setWatch(returnToClock, BTN1);
}
} }

View File

@ -2,7 +2,7 @@
"id": "iconlaunch", "id": "iconlaunch",
"name": "Icon Launcher", "name": "Icon Launcher",
"shortName" : "Icon launcher", "shortName" : "Icon launcher",
"version": "0.04", "version": "0.05",
"icon": "app.png", "icon": "app.png",
"description": "A launcher inspired by smartphones, with an icon-only scrollable menu.", "description": "A launcher inspired by smartphones, with an icon-only scrollable menu.",
"tags": "tool,system,launcher", "tags": "tool,system,launcher",

View File

@ -28,6 +28,10 @@
/*LANG*/"One click exit": { /*LANG*/"One click exit": {
value: settings.oneClickExit == true, value: settings.oneClickExit == true,
onchange: (m) => { save("oneClickExit", m) } onchange: (m) => { save("oneClickExit", m) }
},
/*LANG*/"Fastload": {
value: settings.fastload == true,
onchange: (m) => { save("fastload", m) }
} }
}; };
E.showMenu(appMenu); E.showMenu(appMenu);

10
apps/primetime/README.md Normal file
View File

@ -0,0 +1,10 @@
# App Name
Watchface that displays time and the prime factors of the "military time" (i.e. 21:05 => 2105, shows prime factors of 2105 which are 5 & 421). Displays "Prime Time!" if prime.
![image](https://user-images.githubusercontent.com/115424919/194777279-7f5e4d2a-f475-4099-beaf-38db5b460714.png)
## Creator
Adapted from simplestclock by [Eve Bury](https://www.github.com/eveeeon)

BIN
apps/primetime/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -0,0 +1,15 @@
{ "id": "primetime",
"name": "Prime Time Clock",
"version": "0.01",
"type": "clock",
"description": "A clock that tells you the primes of the time",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"tags": "clock",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"primetime.app.js","url":"primetime.js"},
{"name":"primetime.img","url":"primetime-icon.js","evaluate":true}
]
}

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgVVABVADJMBBf4L/Bf4LMgtQgIHCitAqoHBoEv+EHwALBv/S//4BYO//svwELoP//X/+gLB2E93+Ah9B9f+//QBYMVvv3C4XvvwLDl/0q+AgsB998qt4F4XgHYIXB/1+6ALC//93/4F4I7CI4QLBAIMLoF/6ABBBYNVqgBBgprCAIKz0qkAooLHgP8gXvvALH/EL7e4BY+tz/+vovH3PR1++L9YL/BYdVABQ="))

View File

@ -0,0 +1,89 @@
const h = g.getHeight();
const w = g.getWidth();
// creates a list of prime factors of n and outputs them as a string, if n is prime outputs "Prime Time!"
function primeFactors(n) {
const factors = [];
let divisor = 2;
while (n >= 2) {
if (n % divisor == 0) {
factors.push(divisor);
n = n / divisor;
} else {
divisor++;
}
}
if (factors.length === 1) {
return "Prime Time!";
}
else
return factors.toString();
}
// converts time HR:MIN to integer HRMIN e.g. 15:35 => 1535
function timeToInt(t) {
var arr = t.split(':');
var intTime = parseInt(arr[0])*100+parseInt(arr[1]);
return intTime;
}
function draw() {
var date = new Date();
var timeStr = require("locale").time(date,1);
var primeStr = primeFactors(timeToInt(timeStr));
g.reset();
g.setColor(0,0,0);
g.fillRect(Bangle.appRect);
g.setFont("6x8", w/30);
g.setFontAlign(0, 0);
g.setColor(100,100,100);
g.drawString(timeStr, w/2, h/2);
g.setFont("6x8", w/60);
g.drawString(primeStr, w/2, 3*h/4);
queueDraw();
}
// timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
g.clear();
// Show launcher when middle button pressed
// Bangle.setUI("clock");
// use clockupdown as it tests for issue #1249
Bangle.setUI("clockupdown", btn=> {
draw();
});
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
draw();

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -2,3 +2,4 @@
0.02: Make Bangle.js 2 compatible 0.02: Make Bangle.js 2 compatible
0.03: Silently use built in heading when no magnav calibration file is present 0.03: Silently use built in heading when no magnav calibration file is present
0.04: Move waypoints.json (and editor) to 'waypoints' app 0.04: Move waypoints.json (and editor) to 'waypoints' app
0.05: Fix not displaying of wpindex = 0

View File

@ -263,7 +263,7 @@ function nextwp(inc){
} }
function doselect(){ function doselect(){
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) { if (selected && wpindex>=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon}; waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex]; wp = waypoints[wpindex];
require("waypoints").save(waypoints); require("waypoints").save(waypoints);

View File

@ -1,7 +1,7 @@
{ {
"id": "waypointer", "id": "waypointer",
"name": "Way Pointer", "name": "Way Pointer",
"version": "0.04", "version": "0.05",
"description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation", "description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation",
"icon": "waypointer.png", "icon": "waypointer.png",
"tags": "tool,outdoors,gps", "tags": "tool,outdoors,gps",

View File

@ -78,7 +78,7 @@ const APP_KEYS = [
'supports', 'allow_emulator', 'supports', 'allow_emulator',
'dependencies' 'dependencies'
]; ];
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports']; const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports', 'noOverwrite'];
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate']; const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports' const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale","settings","waypoints"]; // values allowed for "type" field const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale","settings","waypoints"]; // values allowed for "type" field

2
core

@ -1 +1 @@
Subproject commit 6857957f5aedfd9d175ecbf8e49d08bb167b8128 Subproject commit 76419750083a88ee7a569db3975ae1bdd6dc155a