1
0
Fork 0
David Peer 2021-11-25 18:49:46 +01:00
commit d99cea9ae0
14 changed files with 135 additions and 44 deletions

View File

@ -32,7 +32,7 @@
{
"id": "messages",
"name": "Messages",
"version": "0.05",
"version": "0.07",
"description": "App to display notifications from iOS and Gadgetbridge",
"icon": "app.png",
"type": "app",
@ -41,18 +41,19 @@
"readme": "README.md",
"storage": [
{"name":"messages.app.js","url":"app.js"},
{"name":"messages.settings.js","url":"settings.js"},
{"name":"messages.img","url":"app-icon.js","evaluate":true},
{"name":"messages.wid.js","url":"widget.js"},
{"name":"messages","url":"lib.js"}
],
"data": [{"name":"messages.json"}],
"data": [{"name":"messages.json"},{"name":"messages.settings.json"}],
"sortorder": -9
},
{
"id": "android",
"name": "Android Integration",
"shortName": "Android",
"version": "0.03",
"version": "0.04",
"description": "(BETA) App to display notifications from Gadgetbridge on Android. This will eventually replace the Gadgetbridge widget.",
"icon": "app.png",
"tags": "tool,system,messages,notifications",
@ -60,6 +61,7 @@
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"android.app.js","url":"app.js"},
{"name":"android.settings.js","url":"settings.js"},
{"name":"android.img","url":"app-icon.js","evaluate":true},
{"name":"android.boot.js","url":"boot.js"}
],
@ -551,8 +553,8 @@
{
"id": "cubescramble",
"name": "Cube Scramble",
"version":"0.03",
"description": "A random scramble generator for the 3x3 Rubik's cube",
"version":"0.04",
"description": "A random scramble generator for the 3x3 Rubik's cube with a basic timer",
"icon": "cube-scramble.png",
"tags": "",
"supports" : ["BANGLEJS","BANGLEJS2"],

View File

@ -2,3 +2,4 @@
0.02: Remove messages on disconnect
Fix music control
0.03: Handling of message actions (ok/clear)
0.04: Android icon now goes to settings page with 'find phone'

View File

@ -1,2 +1,3 @@
// Config app not implemented yet
setTimeout(()=>load("messages.app.js"),10);
Bangle.loadWidgets();
Bangle.drawWidgets();
eval(require("Storage").read("android.settings.js"))(()=>load());

18
apps/android/settings.js Normal file
View File

@ -0,0 +1,18 @@
(function(back) {
function gb(j) {
Bluetooth.println(JSON.stringify(j));
}
var mainmenu = {
"" : { "title" : "Android" },
"< Back" : back,
"Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" },
"Find Phone" : () => E.showMenu({
"" : { "title" : "Find Phone" },
"< Back" : ()=>E.showMenu(mainmenu),
"On" : _=>gb({t:"findPhone",n:true}),
"Off" : _=>gb({t:"findPhone",n:false}),
}),
"Messages" : ()=>load("messages.app.js")
};
E.showMenu(mainmenu);
})

View File

@ -1,3 +1,4 @@
0.01: Initial Release
0.02: Replace icon with one found on https://icons8.com
0.03: Re-render icon fixing display in settings
0.04: Improved UX and display solve time

View File

@ -1,12 +1,11 @@
# Cube Scramble
A random scramble generator for the 3x3 Rubik's cube
A random scramble generator for the 3x3 Rubik's cube with a basic timer.
## Future features
I'm keen to complete this project with
* Add a timer
* Add the ability for times to be stored and exported
## Requests

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,4 +1,3 @@
// Scramble code from: https://raw.githubusercontent.com/bjcarlson42/blog-post-sample-code/master/Rubik's%20Cube%20JavaScript%20Scrambler/part_two.js
const makeScramble = () => {
const options = ["F", "F2", "F'", "R", "R2", "R'", "U", "U2", "U'", "B", "B2", "B'", "L", "L2", "L'", "D", "D2", "D'"];
@ -59,16 +58,36 @@ const getRandomInt = max => Math.floor(Math.random() * Math.floor(max)); // retu
const getRandomIntBetween = (min, max) => Math.floor(Math.random() * (max - min) + min);
const presentScramble = () => {
g.clear();
E.showMessage(makeScramble().join(" "));
showPrompt(makeScramble().join(" "), {
buttons: {"solve": true, "reset": false}
}).then((v) => {
if (v) {
const start = new Date();
showPrompt(" ", {
buttons: {"stop": true}
}).then(() => {
const time = parseFloat(((new Date()).getTime() - start.getTime()) / 1000);
showPrompt(String(time.toFixed(3)), {
buttons: {"next": true}
}).then(() => {
presentScramble();
});
});
} else {
presentScramble();
}
});
};
const showPrompt = (text, options = {}) => {
options.title = options.title || "cube scramble";
return E.showPrompt(text, options);
};
const init = () => {
Bangle.setLCDTimeout(0);
Bangle.setLCDPower(1);
presentScramble();
setWatch(() => {
presentScramble();
}, BTN1, {repeat:true});
};
init();

View File

@ -23,6 +23,7 @@
}
var mainmenu = {
"" : { "title" : "Gadgetbridge" },
"< Back" : back,
"Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" },
"Show Icon" : {
value: settings().showIcon,
@ -34,8 +35,7 @@
value: !!settings().hrm,
format: v => v?"Yes":"No",
onchange: v => updateSetting('hrm', v)
},
"< Back" : back,
}
};
var findPhone = {

View File

@ -3,3 +3,8 @@
0.03: Fixes for Bangle.js 1
0.04: Add require("messages").clearAll()
0.05: Handling of message actions (ok/clear)
0.06: New messages now go at the start (fix #898)
Answering true/false now exits the messages app if no new messages
Back now marks a message as read
Clicking top-left opens a menu which allows you to delete a message or mark unread
0.07: Added settings menu with option to choose vibrate pattern and frequency (fix #909)

View File

@ -46,7 +46,10 @@ var MESSAGES = require("Storage").readJSON("messages.json",1)||[];
if (!Array.isArray(MESSAGES)) MESSAGES=[];
var onMessagesModified = function(msg) {
// TODO: if new, show this new one
if (msg.new) Bangle.buzz();
if (msg.new) {
if (WIDGETS["messages"]) WIDGETS["messages"].buzz();
else Bangle.buzz();
}
showMessage(msg.id);
};
function saveMessages() {
@ -111,7 +114,7 @@ function showMapMessage(msg) {
msg.new = false;
saveMessages();
layout = undefined;
checkMessages();
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1});
});
}
@ -126,7 +129,7 @@ function showMusicMessage(msg) {
msg.new = false;
saveMessages();
layout = undefined;
checkMessages();
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1});
}
layout = new Layout({ type:"v", c: [
{type:"h", fillx:1, bgCol:colBg, c: [
@ -148,6 +151,22 @@ function showMusicMessage(msg) {
layout.render();
}
function showMessageSettings(msg) {
E.showMenu({"":{"title":"Message"},
"< Back" : () => showMessage(msg.id),
"Delete" : () => {
MESSAGES = MESSAGES.filter(m=>m.id!=msg.id);
saveMessages();
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0});
},
"Mark Unread" : () => {
msg.new = true;
saveMessages();
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0});
},
});
}
function showMessage(msgid) {
var msg = MESSAGES.find(m=>m.id==msgid);
if (!msg) return checkMessages(); // go home if no message found
@ -163,30 +182,30 @@ function showMessage(msgid) {
title = g.wrapString(title, w).join("\n");
}
var buttons = [
{type:"btn", src:getBackImage(), cb:()=>checkMessages(true)}, // back
msg.new?{type:"btn", src:atob("HRiBAD///8D///wj///Fj//8bj//x3z//Hvx/8/fx/j+/x+Ad/B4AL8Rh+HxwH+PHwf+cf5/+x/n/PH/P8cf+cx5/84HwAB4fgAD5/AAD/8AAD/wAAD/AAAD8A=="), cb:()=>{
{type:"btn", src:getBackImage(), cb:()=>{
msg.new = false; // read mail
saveMessages();
checkMessages();
}}:{}
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:1});
}} // back
];
if (msg.positive) {
buttons.push({type:"btn", src:getPosImage(), cb:()=>{
msg.new = false; saveMessages();
Bangle.messageResponse(msg,true);
checkMessages();
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1});
}});
}
if (msg.negative) {
buttons.push({type:"btn", src:getNegImage(), cb:()=>{
console.log("Response");
msg.new = false; saveMessages();
Bangle.messageResponse(msg,true);
checkMessages();
Bangle.messageResponse(msg,false);
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1});
}});
}
layout = new Layout({ type:"v", c: [
{type:"h", fillx:1, bgCol:colBg, c: [
{ type:"img", src:getMessageImage(msg), pad:2 },
{ type:"btn", src:getMessageImage(msg), cb:()=>showMessageSettings(msg) },
{ type:"v", fillx:1, c: [
{type:"txt", font:fontMedium, label:msg.src||"Message", bgCol:colBg, fillx:1, pad:2 },
title?{type:"txt", font:titleFont, label:title, bgCol:colBg, fillx:1, pad:2 }:{},
@ -199,28 +218,37 @@ function showMessage(msgid) {
layout.render();
}
function checkMessages(forceShowMenu) {
/* options = {
clockIfNoMsg : bool
clockIfAllRead : bool
showMsgIfUnread : bool
}
*/
function checkMessages(options) {
options=options||{};
// If no messages, just show 'no messages' and return
if (!MESSAGES.length) {
if (forceShowMenu) return E.showPrompt("No Messages",{
if (!options.clockIfNoMsg) return E.showPrompt("No Messages",{
title:"Messages",
img:require("heatshrink").decompress(atob("kkk4UBrkc/4AC/tEqtACQkBqtUDg0VqAIGgoZFDYQIIM1sD1QAD4AIBhnqA4WrmAIBhc6BAWs8AIBhXOBAWz0AIC2YIC5wID1gkB1c6BAYFBEQPqBAYXBEQOqBAnDAIQaEnkAngaEEAPDFgo+IKA5iIOhCGIAFb7RqAIGgtUBA0VqobFgNVA")),
buttons : {"Ok":1}
}).then(() => { load() });
load();
return;
return load();
}
// we have >0 messages
var newMessages = MESSAGES.filter(m=>m.new);
// If we have a new message, show it
if (!forceShowMenu) {
var newMessages = MESSAGES.filter(m=>m.new);
if (newMessages.length)
return showMessage(newMessages[0].id);
}
if (options.showMsgIfUnread && newMessages.length)
return showMessage(newMessages[0].id);
// no new messages - go to clock?
if (options.clockIfAllRead && newMessages.length==0)
return load();
// Otherwise show a menu
E.showScroller({
h : 48,
c : Math.min(MESSAGES.length+1,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
c : Math.max(MESSAGES.length+1,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
draw : function(idx, r) {"ram"
var msg = MESSAGES[idx-1];
if (msg && msg.new) g.setBgColor(colBg);
@ -239,7 +267,7 @@ function checkMessages(forceShowMenu) {
x += 50;
}
var m = msg.title+"\n"+msg.body;
if (msg.src) g.setFontAlign(1,-1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+2);
if (msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2);
if (title) g.setFontAlign(-1,-1).setFont(fontBig).drawString(title, x,r.y+2);
if (body) {
g.setFontAlign(-1,-1).setFont("6x8");
@ -261,4 +289,6 @@ function checkMessages(forceShowMenu) {
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
checkMessages(true); // force showing a menu
setTimeout(() => {
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:1});
},10); // if checkMessages wants to 'load', do that

View File

@ -17,7 +17,10 @@ exports.pushMessage = function(event) {
mIdx=-1;
} else { // add/modify
if (event.t=="add") event.new=true; // new message
if (mIdx<0) mIdx=messages.push(event)-1;
if (mIdx<0) {
mIdx=0;
messages.unshift(event); // add new messages to the beginning
}
else Object.assign(messages[mIdx], event);
}
require("Storage").writeJSON("messages.json",messages);

View File

@ -1,3 +1,4 @@
WIDGETS["messages"]={area:"tl",width:0,draw:function() {
if (!this.width) return;
var c = (Date.now()-this.t)/1000;
@ -5,9 +6,11 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
g.clearRect(this.x,this.y,this.x+this.width,this.y+23);
g.setFont("6x8:1x2").setFontAlign(0,0).drawString("MESSAGES", this.x+this.width/2, this.y+12);
//if (c<60) Bangle.setLCDPower(1); // keep LCD on for 1 minute
if (c<120 && (Date.now()-this.l)>4000) {
let settings = require('Storage').readJSON("messages.settings.json", true) || {};
if (settings.repeat===undefined) settings.repeat = 4;
if (c<120 && (Date.now()-this.l)>settings.repeat*1000) {
this.l = Date.now();
Bangle.buzz(); // buzz every 4 seconds
WIDGETS["messages"].buzz(); // buzz every 4 seconds
}
setTimeout(()=>WIDGETS["messages"].draw(), 1000);
},show:function() {
@ -21,4 +24,13 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
delete WIDGETS["messages"].l;
WIDGETS["messages"].width=0;
Bangle.drawWidgets();
},buzz:function() {
let v = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || ".";
function b() {
var c = v[0];
v = v.substr(1);
if (c==".") Bangle.buzz().then(()=>setTimeout(b,100));
if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100));
}
b();
}};