Merge branch 'pinq--master'

pull/2693/head
Gordon Williams 2023-04-13 10:36:25 +01:00
commit d06cedcc5e
35 changed files with 588 additions and 61 deletions

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("j0ewkBiIAxHIQMJiBJEIxAaCAIQfHDgIUFDwwNCHYgVFiAVBHYgIDEghKCCIQGCFYoaDAYgORGIJ2DBwYIBHgQOPgAOIPIYOGAgQOFFgh7DHZQeDBwhoFQgh3JEAgOFFoqkHYRzgOfx4bCJ4gNGSIaJEABA7EAGA")) require("heatshrink").decompress(atob("mEwwMB/4AFgYCB4H//kAAoMAn/w+IFBx8P8fjAoPH4/n4/gg/j8/Px4rB+Pz58ch/wnHzz0wv/+hl5zlhDoOGnOY44FB8cZyOP/1/+OJwcfAoP44OGn4FB/lh5giBAIMz7n/AoP/nf4Aocf/IFDz5YBAoWP+YFD54FFMgIFD84FD84FM/0AApKfDApiaCAAJBCApKyCWgRlBAAWfOIIACj/8Aoc//g/BJ4KTBn4FBBIUfAoIbCx4CBFoUHAQPgDIMhAoOEV4NwVgMOn/4/jdBn8fDILpBUIfwh5TBIAYABA="))

View File

@ -0,0 +1,3 @@
0.01: New face :)
0.02: code improvements
0.03: code improvments to queuedraw and draw

View File

@ -0,0 +1,18 @@
# Encouragement & Positivity Clock
Tap on the watch for a note of encouragement
## Features
Pretty backgrounds
<img width="181" alt="Screenshot 2023-03-28-2" src="https://user-images.githubusercontent.com/44651387/228306964-be0b1d46-aee7-4562-9953-4e461954d41b.png">
<img width="181" alt="Screenshot 2023-03-28-1" src="https://user-images.githubusercontent.com/44651387/228306967-768e0315-8652-4043-9c98-ebf0c72fd2a4.png">
## Requests
If you have any issues or would like to suggest an encouraging note, please tweet me!
## Creator
[Eleanor Tayam](http://twitter.com/elykittytee)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwMB/4AFgYCB4H//kAAoMAn/w+IFBx8P8fjAoPH4/n4/gg/j8/Px4rB+Pz58ch/wnHzz0wv/+hl5zlhDoOGnOY44FB8cZyOP/1/+OJwcfAoP44OGn4FB/lh5giBAIMz7n/AoP/nf4Aocf/IFDz5YBAoWP+YFD54FFMgIFD84FD84FM/0AApKfDApiaCAAJBCApKyCWgRlBAAWfOIIACj/8Aoc//g/BJ4KTBn4FBBIUfAoIbCx4CBFoUHAQPgDIMhAoOEV4NwVgMOn/4/jdBn8fDILpBUIfwh5TBIAYABA="))

73
apps/encourageclk/app.js Normal file

File diff suppressed because one or more lines are too long

BIN
apps/encourageclk/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,17 @@
{
"id": "encourageclk",
"name": "Encouragement & Positivity Clock",
"shortName":"Encouragement Clock",
"version": "0.03",
"description": "Tap on the watch for a note of encouragement",
"icon": "app.png",
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"allow_emulator": true,
"readme":"README.md",
"storage": [
{"name":"encourageclk.app.js","url":"app.js"},
{"name":"encourageclk.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -46,7 +46,6 @@
var $name = document.getElementById('add_product_name') var $name = document.getElementById('add_product_name')
var $form = document.getElementById('add_product_form') var $form = document.getElementById('add_product_form')
var $button = document.getElementById('add_product_button')
var $quantity = document.getElementById('add_product_quantity') var $quantity = document.getElementById('add_product_quantity')
var $list = document.getElementById('products') var $list = document.getElementById('products')
var $reset = document.getElementById('reset') var $reset = document.getElementById('reset')
@ -68,7 +67,8 @@
ok: false ok: false
}) })
renderProducts() renderProducts();
window.scrollTo(0, document.body.scrollHeight);
$name.value = '' $name.value = ''
$quantity.value = 1 $quantity.value = 1
}) })

View File

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

View File

@ -0,0 +1,17 @@
# Long Press, Buzz!
Buzz at boot after a long press to indicate the watch was reinitiated at your command.
To infinity and beyond, space ranger!
## Usage
Just install and it will run as boot code.
## Requests
Mention @[thyttan](https://github.com/thyttan) in an issue to the official [BangleApps repository](https://github.com/espruino/BangleApps/issues) for feature requests and bug reports.
## Creators
[thyttan](https://github.com/thyttan) and [Gordon Williams](https://github.com/gfwilliams).

BIN
apps/longpressbuzz/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
if (BTN.read()) Bangle.buzz(80,0.40);

View File

@ -0,0 +1,14 @@
{ "id": "longpressbuzz",
"name": "Long Press, Buzz!",
"shortName":"LPB",
"version":"0.01",
"description": "Buzz at boot after a long press to indicate the watch was reinitiated at your command.",
"icon": "app.png",
"type": "bootloader",
"tags": "system",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"longpressbuzz.0.boot.js","url":"boot.js"}
]
}

View File

@ -1 +1,2 @@
0.01: First rev. Could use a little optimization love. 0.01: First rev. Could use a little optimization love.
0.02: Added battery ring, bubble background for numbers, settings and little optimaztion love.

View File

@ -1,11 +1,16 @@
# Rings watchface # Rings watchface
Ring based watchface, read from the outside in. When the watch is unlocked the circles shrink to show the date ring. Ring based watchface, read from the outside in. When the watch is unlocked the circles shrink to show the date ring.
Contributors: Amos Blanton, pinq-. Inspired by Rinkulainen by Julio Kallio.
By Amos Blanton, inspired by and remixed from Rinkulainen by Jukio Kallio.
![](screenshot1.png) ![](screenshot1.png)
View when watch is locked. View when watch is locked.
![](screenshot2.png) ![](screenshot5.png)
Watch unlocked, showing the date. View when watch is locked with numbers and bubble settings on
![](screenshot3.png)
Watch unlocked, showing the date.

View File

@ -2,20 +2,30 @@
// for Bangle.js 2 // for Bangle.js 2
// by Amos Blanton // by Amos Blanton
// Remixed from / inspired by Rinkulainen watch face by Jukio Kallio // Remixed from / inspired by Rinkulainen watch face by Jukio Kallio
// 2023: pinq- added new futures
// To Do: // To Do:
// Make Month / year text buffer 1/2 size // Make Month / year text buffer 1/2 size
// Optimize text positioning transforms // Optimize text positioning transforms
const watch = { const watch = {
x:0, y:0, w:0, h:0,
color:"#000000", color:"#000000",
dateRing : { size:109, weight:20, color:"#00FF00", cursor:14, numbers: true }, dateRing : { size:109, weight:20, color:"#00FF00", numbers: false, range: 30 },
hourRing : { size:85, weight:20, color:"#00FFFF", cursor:14, numbers: true }, hourRing : { size:82, weight:20, color:"#00FFFF", numbers: false, range: 12},
minuteRing : { size:61, weight:20, color:"#FFFF00", cursor:14, numbers: true }, minuteRing : { size:55, weight:18, color:"#FFFF00", numbers: false, range: 60},
screen : { width:g.getWidth(), height:g.getHeight(), centerX: g.getWidth() *0.5, centerY: g.getHeight() * 0.5, cursor: 14, font:"6x8:2" }, batteryRing: { size :30, weight:10, color:"#ff3300", numbers: false, range: 100},
screen : { width:g.getWidth(), height:g.getHeight(), centerX: g.getWidth() *0.5, centerY: g.getHeight() * 0.5, cursor: 14, font:"Vector:18", bubble:false },
}; };
var settings = require('Storage').readJSON("rings.settings.json", true) || {};
if(settings.minute){
watch.minuteRing.numbers = settings.minute.numbers;
watch.hourRing.numbers = settings.hour.numbers;
watch.dateRing.numbers = settings.date.numbers;
watch.screen.bubble = settings.bubble;
}
delete settings;
const month= ["JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY", const month= ["JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY",
"AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"]; "AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"];
@ -45,31 +55,77 @@ function queueDraw() {
}, wait - (Date.now() % wait)); }, wait - (Date.now() % wait));
} }
// Draws a time circle (date, hours, minutes) // Draws a circles (date, hours, minutes)
function drawTimeCircle(color, size, weight, range, value ) { function drawCircle(ringValues, offset, value ) {
// variables for vertex transformations and positioning time // variables for vertex transformations and positioning time
var tver, tobj, tran; let tver, tobj, tran;
var ttime = (value / range) * (Math.PI * 2); let ttime = (value / ringValues.range) * (Math.PI * 2);
// draw circle and line // draw circle
g.setColor(color).fillCircle(watch.screen.centerX, watch.screen.centerY, size); g.setColor(ringValues.color).fillCircle(watch.screen.centerX, watch.screen.centerY, ringValues.size + offset);
g.setColor("#000000").fillCircle(watch.screen.centerX, watch.screen.centerY, size - weight); g.setColor("#000000").fillCircle(watch.screen.centerX, watch.screen.centerY, ringValues.size - ringValues.weight + offset);
tver = [-watch.screen.cursor, 0, watch.screen.cursor, 0, watch.screen.cursor, -size*1.01, -watch.screen.cursor, -size*1.05];
tobj = { x:watch.screen.centerX, y:watch.screen.centerY, scale:1, rotate:ttime }; tobj = { x:watch.screen.centerX, y:watch.screen.centerY, scale:1, rotate:ttime };
tran = g.transformVertices(tver, tobj); if(watch.screen.bubble){
g.fillPoly(tran); tver = [-1, 0, 1, 0, 1, -ringValues.size-offset, -1, -(ringValues.size + offset -21)];
tran = g.transformVertices(tver, tobj);
if(ringValues.numbers){
g.setColor("#000000").fillCircle((tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2, 17 + offset/10);
}else{
g.setColor("#000000").fillCircle((tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2, 10 + offset/10);
}
}else{
if(ringValues.numbers){
tver = [-watch.screen.cursor, 0, watch.screen.cursor, 0, watch.screen.cursor, -ringValues.size*1.01 - offset, -watch.screen.cursor, -ringValues.size*1.05 - offset];
}else{
tver = [-watch.screen.cursor * 0.4, 0, watch.screen.cursor * 0.4, 0, watch.screen.cursor *0.4, -ringValues.size*1.01 - offset, -watch.screen.cursor*0.4, -ringValues.size*1.05 - offset];
}
tran = g.transformVertices(tver, tobj);
g.fillPoly(tran);
}
// Draw numbers // Draw numbers
g.setFontAlign(0,0).setFont(watch.screen.font, 2).setColor(1,1,1); if(ringValues.numbers){
// size - 21 is the right offset to get the numbers aligned in the circle. // size - 21 is the right offset to get the numbers aligned in the circle.
tver = [-1, 0, 1, 0, 1, -size, -1, -(size -21)]; tver = [-1, 0, 1, 0, 1, -ringValues.size-offset, -1, -(ringValues.size + offset -21)];
tran = g.transformVertices(tver, tobj); tran = g.transformVertices(tver, tobj);
g.setColor(1,1,1); //g.setColor(1,1,1);
g.drawString(value, (tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2 ); g.setFontAlign(0,0).setFont(watch.screen.font, 2).setColor(1,1,1);
g.drawString(value, (tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2 );
}
}
// For battery disable
function drawArc(percent, color, ArchR) {
let offset = 0;
let end = 360;
let radius = ArchR + 2;
if (percent <= 0) return; // no gauge needed
if (percent > 1) percent = 1;
let startRotation = -offset;
let endRotation = startRotation - (end * percent);
g.setColor(color);
// convert to radians
startRotation *= Math.PI / 180;
let amt = Math.PI / 10;
endRotation = (endRotation * Math.PI / 180) - amt;
// all we need to draw is an arc, because we'll fill the center
let poly = [watch.screen.centerX, watch.screen.centerY];
for (let r = startRotation; r > endRotation; r -= amt)
poly.push(
watch.screen.centerX - radius * Math.sin(r),
watch.screen.centerY - radius * Math.cos(r)
);
g.fillPoly(poly);
g.setColor("#000000").fillCircle(watch.screen.centerX, watch.screen.centerY, ArchR - 10);
g.setColor(color).fillCircle(watch.screen.centerX - (radius -5) * Math.sin(endRotation + amt), watch.screen.centerY - (radius -5) * Math.cos(endRotation + amt), 4);
} }
// Draws text for month and year in date circle // Draws text for month and year in date circle
@ -80,16 +136,16 @@ function drawMonthCircleText( text, circleSize, range, value){
monthCircleTextBuffer.clear(); monthCircleTextBuffer.clear();
monthCircleTextBuffer.fillRect(0,0,watch.screen.width,watch.screen.height); monthCircleTextBuffer.fillRect(0,0,watch.screen.width,watch.screen.height);
var tver, tobj, tran; var tver, tobj, tran;
// From here: https://forum.espruino.com/comments/16781795/ // From here: https://forum.espruino.com/comments/16781795/
var gr = Graphics.createArrayBuffer(24,16,1,{msb:true}); var gr = Graphics.createArrayBuffer(24,16,1,{msb:true});
var grimg = gr.asImage(); var grimg = gr.asImage();
grimg.transparent = 1; grimg.transparent = 1;
monthCircleTextBuffer.setColor(0,0,0); monthCircleTextBuffer.setColor(1,1,1);
for(z=0; z < text.length; z++){ for(z=0; z < text.length; z++){
tobj = { x:watch.screen.centerX, y:watch.screen.centerY, scale:1, rotate: ((z + 1) / range) * (Math.PI * 2) }; tobj = { x:watch.screen.centerX, y:watch.screen.centerY, scale:1, rotate: ((z + 1) / range) * (Math.PI * 2) };
tver = [-1, 0, 1, 0, 1, -circleSize, -1, -(circleSize -21)]; tver = [-1, 0, 1, 0, 1, -circleSize, -1, -(circleSize -21)];
tran = monthCircleTextBuffer.transformVertices(tver, tobj); tran = monthCircleTextBuffer.transformVertices(tver, tobj);
@ -119,7 +175,6 @@ function drawMonthCircleText( text, circleSize, range, value){
function shrinkCircles(toggle){ function shrinkCircles(toggle){
// If there's a queued draw operation,removeit so animation isn't interrupted. // If there's a queued draw operation,removeit so animation isn't interrupted.
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
var date = new Date(); var date = new Date();
var delta = 1; var delta = 1;
@ -128,6 +183,7 @@ function shrinkCircles(toggle){
counter = 1; counter = 1;
// We're finished, so queue next draw. // We're finished, so queue next draw.
queueDraw(); queueDraw();
if(!toggle) drawArc(E.getBattery() / 100, watch.batteryRing.color, watch.batteryRing.size);
return; return;
} }
@ -142,14 +198,14 @@ function shrinkCircles(toggle){
// Draw the date ring (unless it's the last run of an expansion). // Draw the date ring (unless it's the last run of an expansion).
if(counter < 11 || toggle){ if(counter < 11 || toggle){
drawTimeCircle(watch.dateRing.color, watch.dateRing.size + delta, watch.dateRing.weight, getDays(date.getFullYear(), date.getMonth()+1), date.getDate() );
drawCircle(watch.dateRing, delta, date.getDate());
// Draw month and year in date ring // Draw month and year in date ring
drawMonthCircleText( month[date.getMonth()]+" "+date.getFullYear(), watch.dateRing.size - 24, getDays(date.getFullYear(), date.getMonth()+1), date.getDate()) ; drawMonthCircleText( date.getDate() + " " + month[date.getMonth()] + " " +date.getFullYear(), watch.dateRing.size - 24, getDays(date.getFullYear(), date.getMonth()+1), date.getDate()) ;
} }
drawTimeCircle(watch.hourRing.color, watch.hourRing.size + delta, watch.hourRing.weight, 12, date.getHours() ); drawCircle(watch.hourRing, delta, date.getHours());
drawCircle(watch.minuteRing, delta, date.getMinutes());
drawTimeCircle(watch.minuteRing.color, watch.minuteRing.size + delta, watch.minuteRing.weight, 60, date.getMinutes() );
counter += 1; counter += 1;
setTimeout(shrinkCircles, 10, toggle); setTimeout(shrinkCircles, 10, toggle);
@ -170,19 +226,23 @@ function draw() {
g.fillRect(0, 0, watch.screen.width, watch.screen.height); g.fillRect(0, 0, watch.screen.width, watch.screen.height);
// If unlocked, draw date ring and text and make hour and minute rings smaller // If unlocked, draw date ring and text and make hour and minute rings smaller
var days_month = getDays(date.getFullYear(), date.getMonth()+1);
if(!Bangle.isLocked()){ if(!Bangle.isLocked()){
unLockedOffset = 24; unLockedOffset = 24;
drawTimeCircle(watch.dateRing.color, watch.dateRing.size - unLockedOffset, watch.dateRing.weight, getDays(date.getFullYear(), date.getMonth()+1), date.getDate() ); // if the day has changed
drawMonthCircleText( month[date.getMonth()]+" "+date.getFullYear(), watch.dateRing.size - unLockedOffset, getDays(date.getFullYear(), date.getMonth()+1), date.getDate()) ; if(watch.dateRing.range != days_month) watch.dateRing.range = days_month;
drawCircle(watch.dateRing, -unLockedOffset, date.getDate());
drawMonthCircleText( date.getDate() + " " + month[date.getMonth()] + " " + date.getFullYear(), watch.dateRing.size - 24, getDays(date.getFullYear(), date.getMonth()+1), date.getDate());
}
drawCircle(watch.hourRing, -unLockedOffset, date.getHours());
drawCircle(watch.minuteRing, -unLockedOffset, date.getMinutes());
if(Bangle.isLocked()){
drawArc(E.getBattery() / 100, watch.batteryRing.color, watch.batteryRing.size);
} }
drawTimeCircle(watch.hourRing.color, watch.hourRing.size - unLockedOffset, watch.hourRing.weight, 12, date.getHours() );
drawTimeCircle(watch.minuteRing.color, watch.minuteRing.size -unLockedOffset , watch.minuteRing.weight, 60, date.getMinutes() );
queueDraw(); queueDraw();
} }
// Trigger shrink / expand animation on unlock / lock events //drawArc(E.getBattery() / 100, watch.batteryRing.color, watch.batteryRing.size); Trigger shrink / expand animation on unlock / lock events
Bangle.on('lock', on=>{ Bangle.on('lock', on=>{
if (on) { // locked, expand circles if (on) { // locked, expand circles
counter = 1; counter = 1;
@ -204,7 +264,7 @@ g.clear();
draw(); draw();
// console.log("Whatevs"); // .log("Whatevs");
// Show launcher when middle button pressed // Show launcher when middle button pressed
Bangle.setUI("clock"); Bangle.setUI("clock");

View File

@ -1,16 +1,36 @@
{ "id": "rings", {
"id": "rings",
"name": "Rings - an animated watchface", "name": "Rings - an animated watchface",
"shortName":"Rings", "shortName": "Rings",
"version":"0.01", "version": "0.02",
"description": "Ring based watchface that animates to show the date when unlocked. Inspired by / remixed from Rinkulainen.", "description": "Ring based watchface that animates to show the date when unlocked. Inspired by / remixed from Rinkulainen.",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot1.png"}, {"url":"screenshot2.png"}], "screenshots": [{
"url": "screenshot1.png"
}, {
"url": "screenshot2.png"
}, {
"url": "screenshot3.png"
}],
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports" : ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [{
{"name":"rings.app.js","url":"app.js"}, "name": "rings.app.js",
{"name":"rings.img","url":"app-icon.js","evaluate":true} "url": "app.js"
] },
{
"name": "rings.img",
"url": "app-icon.js",
"evaluate": true
},
{
"name": "rings.settings.js",
"url": "settings.js"
}
],
"data": [{
"name": "rings.settings.json"
}]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
apps/rings/screenshot3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

68
apps/rings/settings.js Normal file
View File

@ -0,0 +1,68 @@
(function(back) {
var FILE = "rings.settings.json";
// Load settings
var settings = Object.assign({
minute: {
numbers: false,
},
hour: {
numbers: false
},
date: {
numbers: false,
},
bubble: false,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings(key, value) {
settings[key] = value;
require("Storage").write("rings.settings.json",settings);
}
var mainmenu = {
"" : { "title" : "Rings" },
"< Back" : () => back(),
/*LANG*/"Minute" : () => E.showMenu({
"" : { "title" : /*LANG*/"Minute" },
"< Back" : ()=>E.showMenu(mainmenu),
/*LANG*/"Number" : {
value: settings.minute.numbers,
onchange: (m) => {
settings.minute.numbers = m,
writeSettings();
},
}
}),
/*LANG*/"Hour" : () => E.showMenu({
"" : { "title" : /*LANG*/"Hour" },
"< Back" : ()=>E.showMenu(mainmenu),
/*LANG*/"Number" : {
value: settings.hour.numbers,
onchange: (m) => {
settings.hour.numbers = m,
writeSettings();
},
}
}),
/*LANG*/"Date" : () => E.showMenu({
"" : { "title" : /*LANG*/"Date" },
"< Back" : ()=>E.showMenu(mainmenu),
/*LANG*/"Number" : {
value: settings.date.numbers,
onchange: (m) => {
settings.date.numbers = m,
writeSettings();
},
}
}),
/*LANG*/"BG Bubble?" : {
value : settings.bubble,
onchange: (m) => {
settings.bubble = m;
writeSettings();
}
},
}
// Show the menu
E.showMenu(mainmenu);
})

View File

@ -1,2 +1,4 @@
0.01: Inital release. 0.01: Inital release.
0.02: Rebasing on latest changes to showScroller_Q3 (https://github.com/espruino/Espruino/commit/2d3c34ef7c2b9fe2118e816aacd2e096adb99596). 0.02: Rebasing on latest changes to showScroller_Q3 (https://github.com/espruino/Espruino/commit/2d3c34ef7c2b9fe2118e816aacd2e096adb99596).
0.03: Rebasing on latest changes to showScroller_Q3 (https://github.com/espruino/Espruino/commit/b6f8105b6348bb6f7cd03ac11efc1f3585c6ad79). Ensure that changing a menu item when half-scrolled off screen doesn't overwrite widgets.

View File

@ -95,7 +95,7 @@ var s = {
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1); g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
}, drawItem : i => { }, drawItem : i => {
var y = idxToY(i); var y = idxToY(i);
g.reset().setClipRect(R.x,y,R.x2,y+options.h); g.reset().setClipRect(R.x,Math.max(y,R.y),R.x2,Math.min(y+options.h,R.y2));
options.draw(i, {x:R.x,y:y,w:R.w,h:options.h}); options.draw(i, {x:R.x,y:y,w:R.w,h:options.h});
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1); g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
}}; }};

View File

@ -1,7 +1,7 @@
{ {
"id": "swscroll", "id": "swscroll",
"name": "Swipe menus", "name": "Swipe menus",
"version": "0.02", "version": "0.03",
"description": "Replace built in E.showScroller to act on swipe instead of drag. Navigate menus in discrete steps instead of a continuous motion.", "description": "Replace built in E.showScroller to act on swipe instead of drag. Navigate menus in discrete steps instead of a continuous motion.",
"readme": "README.md", "readme": "README.md",
"icon": "app.png", "icon": "app.png",

2
apps/tinycmc/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App
0.02: Set API Key through Apploader

7
apps/tinycmc/README.md Normal file
View File

@ -0,0 +1,7 @@
# Tiny Coinmarketcap
A simple app using Gadgedtbridge internet access to fetch current crypto prices from Coinmarketcap.
I'm providing a "free-tier" API Key.
TO-DOs:
- [X] Add own API Key option
- [ ] Add settings for Currency conversion

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

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4kA/4ACBIP/o8242462/k8/gtytlz1s+r2/gv/q9Zpv/m+02pj/AH4AqgMRBhURAAQWGqPxqPwA4UoloABkoLCAIQhIiMQgUo29mtfr6WIBogxDFwNBAIeKwd8x2Mud8lUhBolQLoYABrERjurs/S7F329rs+i+I+EC4JRFxl+lUmvmGAIWKkJhGJ4lI01y2+3s4BB1s7s+o+MVMAhPEll7kd+uYBCvgBB7khyIRCL4cZiMS7YuCAAxnBSAsBJoWKl0sLYYBFluBL4iPCiuNsRdEAIlr3GBR4iQBJoMs7pdEAIjDBkK/ESAUY665BABNn7AvFkWBlEsXYoBFMAMlkPwC4UYRwOru5ZDMY+iitBF4MoPAOBxsjXoktAYMoYYcowOBwEokAXBiq+BL4eywWH2WHA4VrYAMViECgEgYAOIxhXDlF8k0oM4eDxrABkReBkEIj/y1ZVCsRjB2UnL4tYimCO4UBoOdlhdCveDAYMuYId8kq/DGIOI/Hyw6MDAA9t7FI/HwJAUBqOBll7X5WK+PxqK/DgMRimNsS7HAINm3DuBd4hfB+ONxhZEAIl8lVBL4YXCjLwB7eyLxFn7AvHJ4PxlS7BLo2OvmBqJfHAANC02yLotrs+iiMYB4IWCL4QgByMsvlzXYlz7UhLoIBBC4cADwMViNS7drFQNnAYXSFwZeDAAJPCyPxlEsFgIuClouBBoPxRwZgEGAMYwWI81n9sikA9CFwwwCKIWRyOFlEtlANEoAWGMIYyDisVCBAAHKYRjBqOAC6AyECqQA/AH4AJA=="))

118
apps/tinycmc/app.js Normal file
View File

@ -0,0 +1,118 @@
let settings = require("Storage").readJSON("tinycmc.json", 1);
const apiKey = settings.apikey || null;
const cmcBase = 'https://pro-api.coinmarketcap.com/';
const version = ['v1', 'v2'];
const path = {
latest: '/cryptocurrency/listings/latest',
quote: '/cryptocurrency/quotes/latest'
};
let page = 0;
function displayLatest(offset) {
g.clear();
if (page === 0) {
E.showMessage('Getting Top 10');
} else {
E.showMessage(`Getting Top ${offset} to ${offset + 9}`);
}
const uri = offset ? `${cmcBase}${version[0]}${path.latest}?convert=EUR&limit=10&start=${offset}` : `${cmcBase}${version[0]}${path.latest}?convert=EUR&limit=10`;
Bangle.http(uri,
{
method: 'GET',
headers: {
"X-CMC_PRO_API_KEY": apiKey,
}
}).then(data=>{
const result = JSON.parse(data.resp).data;
let menu = {
"" : { title : "-- Select --" },
};
//FIXME: Menu can also take an array of items, this would be easier to compose using map
result.forEach(listing => {
menu[listing.name] = {
title: `${listing.cmc_rank}: ${listing.symbol} ${Number(listing.quote.EUR.percent_change_24h).toFixed(3)}`,
onchange: function() { E.showMenu(); displayQuote(listing.symbol);} };
});
menu.Next = function() {
E.showMenu();
g.clear();
page = page + 1;
displayLatest((page * 10) + 1);
}; // remove the menu
menu.Exit = function() {
E.showMenu();
g.clear();
Bangle.showClock();
}; // remove the menu
g.clear();
E.showMenu(menu);
setWatch(() => {
g.clear();
displayMenu();
}, BTN, {edge:"rising", debounce:50, repeat:true});
}).catch(error=>{
console.log('Error');
console.log(error);
E.showMessage(`${error}\nTo go back press BTN`);
setWatch(() => {
g.clear();
displayMenu();
}, BTN, {edge:"rising", debounce:50, repeat:true});
});
}
function displayQuote(symb) {
g.clear();
E.showMessage(`Getting latest for ${symb}`);
Bangle.http(`${cmcBase}${version[1]}${path.quote}?symbol=${symb}&convert=EUR`,
{
method: 'GET',
headers: {
"X-CMC_PRO_API_KEY": apiKey,
}
}).then(data=>{
g.clear();
const result = JSON.parse(data.resp).data[symb][0];
E.showMessage(`#${result.cmc_rank}: ${result.symbol}\n${Number(result.quote.EUR.price).toFixed(2)}\n%24h:
${result.quote.EUR.percent_change_24h}`);
setWatch(() => {
g.clear();
displayMenu();
}, BTN, {edge:"rising", debounce:50, repeat:true});
}).catch(error=>{
E.showMessage(`${error}\nTo go back press BTN`);
setWatch(() => {
g.clear();
displayMenu();
}, BTN, {edge:"rising", debounce:50, repeat:true});
});
}
function displayMenu() {
if (!apiKey) {
E.showMessage("Please provide a Coinmarketcap API Key");
} else {
// Actually display the menu
E.showMenu({
"" : { title : "-- Select --" }, // options
"Latest": function() { E.showMenu(); displayLatest(); },
"BTC" : function() { E.showMenu(); displayQuote('BTC'); },
"ETH" : function() { E.showMenu(); displayQuote('ETH'); },
"XMR" : function() { E.showMenu(); displayQuote('XMR'); },
"ADA" : function() { E.showMenu(); displayQuote('ADA'); },
"DOGE" : function() { E.showMenu(); displayQuote('DOGE'); },
"LTC" : function() { E.showMenu(); displayQuote('LTC'); },
"Exit" : function() {
E.showMenu();
g.clear();
Bangle.showClock();
}, // remove the menu
});
}
}
function main () {
displayMenu();
}
main ();

BIN
apps/tinycmc/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,63 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<h3>Set Coinmarketcap API key</h3>
<p><input id="apikey" onkeyup="checkInput()" style="width:90%; margin: 3px"></input><button id="upload" class="btn btn-primary">Save key</button></p>
<h4>Where to get your personal API key?</h4>
<p>Go to <a href="https://coinmarketcap.com/api/">https://coinmarketcap.com/api/</a> and sign up for a free account.<br>
After registration you can login and obtain your personal API key.</p>
<script src="../../core/lib/interface.js"></script>
<script>
function checkInput() {
if(document.getElementById("apikey").value==="") {
document.getElementById('upload').disabled = true;
} else {
document.getElementById('upload').disabled = false;
}
}
checkInput();
var settings = {};
function onInit(){
console.log("Loading settings from BangleJs...");
try {
Util.readStorage("tinycmc.json", data=>{
if(data.length > 0){
settings = JSON.parse(data);
console.log("Got settings", settings);
document.getElementById("apikey").value = settings.apikey;
console.log("Loaded apikey from BangleJs.");
checkInput();
}
});
} catch(ex) {
console.log("(Warning) Could not load apikey from BangleJs.");
console.log(ex);
}
}
document.getElementById("upload").addEventListener("click", function() {
try {
settings.apikey = document.getElementById("apikey").value;
Util.showModal("Saving...");
Util.writeStorage("tinycmc.json", JSON.stringify(settings), ()=>{
Util.hideModal();
});
console.log("Sent settings!");
} catch(ex) {
console.log("(Warning) Could not write settings to BangleJs.");
console.log(ex);
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,30 @@
{
"id": "tinycmc",
"name": "Tiny CMC",
"shortName":"tinycmc",
"icon": "icon.png",
"version": "0.02",
"description": "TinyCMC is a bangle.js Coinmarketcap API client",
"type": "app",
"tags": "tools",
"supports": [
"BANGLEJS2"
],
"allow_emulator": false,
"interface": "interface.html",
"readme": "README.md",
"data": [
{"name":"tinycmc.json"}
],
"storage": [
{
"name": "tinycmc.app.js",
"url": "app.js"
},
{
"name": "tinycmc.img",
"url": "app-icon.js",
"evaluate": true
}
]
}

View File

@ -1 +1,2 @@
0.01: New widget 0.01: New widget
0.02: Make color depend on level

View File

@ -1,7 +1,7 @@
{ {
"id": "widbatv", "id": "widbatv",
"name": "Battery Level Widget (Vertical)", "name": "Battery Level Widget (Vertical)",
"version": "0.01", "version": "0.02",
"description": "Slim, vertical battery widget that only takes up 14px", "description": "Slim, vertical battery widget that only takes up 14px",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",

View File

@ -14,6 +14,10 @@ WIDGETS["batv"]={area:"tr",width:14,draw:function() {
} else { } else {
g.clearRect(x,y,x+14,y+24); g.clearRect(x,y,x+14,y+24);
g.setColor(g.theme.fg).fillRect(x+2,y+2,x+12,y+22).clearRect(x+4,y+4,x+10,y+20).fillRect(x+5,y+1,x+9,y+2); g.setColor(g.theme.fg).fillRect(x+2,y+2,x+12,y+22).clearRect(x+4,y+4,x+10,y+20).fillRect(x+5,y+1,x+9,y+2);
g.setColor("#0f0").fillRect(x+4,y+20-(E.getBattery()*16/100),x+10,y+20); var battery = E.getBattery();
if (battery < 20) {g.setColor("#f00");}
else if (battery < 40) {g.setColor(g.theme.dark ? "#ff0" : "#f80");}
else {g.setColor("#0f0");}
g.fillRect(x+4,y+20-(E.getBattery()*16/100),x+10,y+20);
} }
}}; }};