1
0
Fork 0

Merge branch 'master' of https://github.com/espruino/BangleApps into espruino-master

# Conflicts:
#	apps/messages/ChangeLog
#	apps/messages/lib.js
master
Jeroen Peters 2021-12-08 02:39:24 +01:00
commit c9c91871e0
27 changed files with 451 additions and 146 deletions

View File

@ -57,7 +57,7 @@
{
"id": "messages",
"name": "Messages",
"version": "0.09",
"version": "0.10",
"description": "App to display notifications from iOS and Gadgetbridge",
"icon": "app.png",
"type": "app",
@ -146,7 +146,7 @@
{
"id": "setting",
"name": "Settings",
"version": "0.34",
"version": "0.35",
"description": "A menu for setting up Bangle.js",
"icon": "settings.png",
"tags": "tool,system",
@ -263,16 +263,17 @@
"id": "mywelcome",
"name": "Customised Welcome",
"shortName": "My Welcome",
"version": "0.12",
"version": "0.13",
"description": "Appears at first boot and explains how to use Bangle.js. Like 'Welcome', but can be customised with a greeting",
"icon": "app.png",
"tags": "start,welcome",
"supports": ["BANGLEJS"],
"supports": ["BANGLEJS","BANGLEJS2"],
"custom": "custom.html",
"screenshots": [{"url":"bangle1-customized-welcome-screenshot.png"}],
"storage": [
{"name":"mywelcome.boot.js","url":"boot.js"},
{"name":"mywelcome.app.js","url":"app.js"},
{"name":"mywelcome.app.js","url":"app-bangle1.js","supports": ["BANGLEJS"]},
{"name":"mywelcome.app.js","url":"app-bangle2.js","supports": ["BANGLEJS2"]},
{"name":"mywelcome.settings.js","url":"settings.js"},
{"name":"mywelcome.img","url":"app-icon.js","evaluate":true}
],
@ -1955,11 +1956,12 @@
"id": "openstmap",
"name": "OpenStreetMap",
"shortName": "OpenStMap",
"version": "0.10",
"description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are",
"version": "0.11",
"description": "Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are. Once installed this also adds map functionality to `GPS Recorder` and `Recorder` apps",
"icon": "app.png",
"tags": "outdoors,gps",
"tags": "outdoors,gps,osm",
"supports": ["BANGLEJS","BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"}],
"custom": "custom.html",
"customConnect": true,
"storage": [
@ -1989,11 +1991,12 @@
"id": "chronowid",
"name": "Chrono Widget",
"shortName": "Chrono Widget",
"version": "0.03",
"version": "0.04",
"description": "Chronometer (timer) which runs as widget.",
"icon": "app.png",
"tags": "tool,widget",
"supports": ["BANGLEJS","BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"}],
"readme": "README.md",
"storage": [
{"name":"chronowid.wid.js","url":"widget.js"},
@ -4643,23 +4646,6 @@
{"name":"a_speech_timer.img","url":"app-icon.js","evaluate":true}
]
},
{
"id": "sensible",
"name": "SensiBLE",
"shortName": "SensiBLE",
"version": "0.02",
"description": "Collect, display and advertise real-time sensor data.",
"icon": "sensible.png",
"type": "app",
"tags": "tool,sensors",
"supports" : [ "BANGLEJS2" ],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{ "name": "sensible.app.js", "url": "sensible.js" },
{ "name": "sensible.img", "url": "sensible-icon.js", "evaluate": true }
]
},
{ "id": "mylocation",
"name": "My Location",
"shortName":"My Location",
@ -4735,7 +4721,7 @@
{
"id": "weatherClock",
"name": "Weather Clock",
"version": "0.01",
"version": "0.02",
"description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).",
"icon": "app.png",
"screenshots": [{"url":"screens/screen1.png"}],

View File

@ -1,3 +1,5 @@
0.01: New widget and app!
0.02: Setting to reset values, timer buzzes at 00:00 and not later (see readme)
0.03: Display only minutes:seconds when less than 1 hour left
0.03: Display only minutes:seconds when less than 1 hour left
0.04: Change to 7 segment font, move to top widget bar
Better auto-update behaviour, less RAM used

View File

@ -5,14 +5,13 @@ The advantage is, that you can still see your normal watchface and other widgets
The widget is always active, but only shown when the timer is on.
Hours, minutes, seconds and timer status can be set with an app.
When there is less than one seconds left on the timer it buzzes.
When there is less than one second left on the timer it buzzes.
The widget has been tested on Bangle 1 and Bangle 2
## Screenshots
![](chrono_with_wave.jpg)
![](chrono_with_pastel.jpg)
![](screenshot.png)
## Features
@ -28,15 +27,15 @@ There are no settings section in the settings app, timer can be set using an app
* Hours: Set the hours for the timer
* Minutes: Set the minutes for the timer
* Seconds: Set the seconds for the timer
* Timer on: Starts the timer and displays the widget when set to 'On'. You have to leave the app to load the widget which starts the timer. The widget is always there, but only visible when timer is on.
* Timer on: Starts the timer and displays the widget when set to 'On'. You have to leave the app to load the widget which starts the timer. The widget is always there, but only visible when timer is on.
## Releases
* Offifical app loader: https://github.com/espruino/BangleApps/tree/master/apps/chronowid (https://banglejs.com/apps/)
* Official app loader: https://github.com/espruino/BangleApps/tree/master/apps/chronowid (https://banglejs.com/apps/)
* Forked app loader: https://github.com/Purple-Tentacle/BangleApps/tree/master/apps/chronowid (https://purple-tentacle.github.io/BangleApps/index.html#)
* Development: https://github.com/Purple-Tentacle/BangleAppsDev/tree/master/apps/chronowid
## Requests
If you have any feature requests, please write here: http://forum.espruino.com/conversations/345972/
If you have any feature requests, please write here: http://forum.espruino.com/conversations/345972/

View File

@ -3,7 +3,6 @@ Bangle.loadWidgets();
Bangle.drawWidgets();
const storage = require('Storage');
const boolFormat = v => v ? "On" : "Off";
let settingsChronowid;
function updateSettings() {
@ -12,6 +11,7 @@ function updateSettings() {
now.getHours() + settingsChronowid.hours, now.getMinutes() + settingsChronowid.minutes, now.getSeconds() + settingsChronowid.seconds);
settingsChronowid.goal = goal.getTime();
storage.writeJSON('chronowid.json', settingsChronowid);
if (WIDGETS["chronowid"]) WIDGETS["chronowid"].reload();
}
function resetSettings() {
@ -44,6 +44,7 @@ function showMenu() {
timerMenu.started.value = settingsChronowid.started;
}
},
'< Back' : ()=>{load();},
'Reset values': function() {
settingsChronowid.hours = 0;
settingsChronowid.minutes = 0;
@ -84,15 +85,15 @@ function showMenu() {
},
'Timer on': {
value: settingsChronowid.started,
format: boolFormat,
format: v => v ? "On" : "Off",
onchange: v => {
settingsChronowid.started = v;
updateSettings();
}
},
};
timerMenu['-Exit-'] = ()=>{load();};
return E.showMenu(timerMenu);
}
showMenu();
showMenu();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,93 +1,79 @@
(() => {
const storage = require('Storage');
settingsChronowid = storage.readJSON("chronowid.json",1)||{}; //read settingsChronowid from file
var height = 23;
var width = 58;
var settingsChronowid;
var interval = 0; //used for the 1 second interval timer
var now = new Date();
var diff;
var time = 0;
var diff = settingsChronowid.goal - now;
//Convert ms to time
function getTime(t) {
var milliseconds = parseInt((t % 1000) / 100),
seconds = Math.floor((t / 1000) % 60),
minutes = Math.floor((t / (1000 * 60)) % 60),
hours = Math.floor((t / (1000 * 60 * 60)) % 24);
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
return hours + ":" + minutes + ":" + seconds;
return hours.toString().padStart(2,0) + ":" + minutes.toString().padStart(2,0) + ":" + seconds.toString().padStart(2,0);
}
function printDebug() {
print ("Nowtime: " + getTime(now));
print ("Now: " + now);
/*function printDebug() {
print ("Goaltime: " + getTime(settingsChronowid.goal));
print ("Goal: " + settingsChronowid.goal);
print("Difftime: " + getTime(diff));
print("Diff: " + diff);
print ("Started: " + settingsChronowid.started);
print ("----");
}
}*/
//counts down, calculates and displays
function countDown() {
now = new Date();
var now = new Date();
diff = settingsChronowid.goal - now; //calculate difference
WIDGETS["chronowid"].draw();
//time is up
// time is up
if (settingsChronowid.started && diff < 1000) {
Bangle.buzz(1500);
//write timer off to file
settingsChronowid.started = false;
storage.writeJSON('chronowid.json', settingsChronowid);
require('Storage').writeJSON('chronowid.json', settingsChronowid);
clearInterval(interval); //stop interval
interval = undefined;
}
//printDebug();
// calculates width and redraws accordingly
WIDGETS["chronowid"].redraw();
}
// draw your widget
function draw() {
if (!settingsChronowid.started) {
width = 0;
return; //do not draw anything if timer is not started
}
g.reset();
if (diff >= 0) {
if (diff < 3600000) { //less than 1 hour left
width = 58;
g.clearRect(this.x,this.y,this.x+width,this.y+height);
g.setFont("6x8", 2);
g.drawString(getTime(diff).substring(3), this.x+1, this.y+5); //remove hour part 00:00:00 -> 00:00
}
if (diff >= 3600000) { //one hour or more left
width = 48;
g.clearRect(this.x,this.y,this.x+width,this.y+height);
g.setFont("6x8", 1);
g.drawString(getTime(diff), this.x+1, this.y+((height/2)-4)); //display hour 00:00:00
}
}
// not needed anymoe, because we check if diff < 1000 now, so 00:00 is displayed.
// else {
// width = 58;
// g.clearRect(this.x,this.y,this.x+width,this.y+height);
// g.setFont("6x8", 2);
// g.drawString("END", this.x+15, this.y+5);
// }
}
if (settingsChronowid.started) interval = setInterval(countDown, 1000); //start countdown each second
// add the widget
WIDGETS["chronowid"]={area:"bl",width:width,draw:draw,reload:function() {
reload();
Bangle.drawWidgets(); // relayout all widgets
WIDGETS["chronowid"]={area:"tl",width:0,draw:function() {
if (!this.width) return;
g.reset().setFontAlign(0,0).clearRect(this.x,this.y,this.x+this.width,this.y+23);
//g.drawRect(this.x,this.y,this.x+this.width-1, this.y+23);
var scale;
var timeStr;
if (diff < 3600000) { //less than 1 hour left
width = 58;
scale = 2;
timeStr = getTime(diff).substring(3); // remove hour part 00:00:00 -> 00:00
} else { //one hour or more left
width = 48;
scale = 1;
timeStr = getTime(diff); //display hour 00:00:00 but small
}
// Font5x9Numeric7Seg - just build this in as it's tiny
g.setFontCustom(atob("AAAAAAAAAAIAAAQCAQAAAd0BgMBdwAAAAAAAdwAB0RiMRcAAAERiMRdwAcAQCAQdwAcERiMRBwAd0RiMRBwAAEAgEAdwAd0RiMRdwAcERiMRdwAFAAd0QiEQdwAdwRCIRBwAd0BgMBAAABwRCIRdwAd0RiMRAAAd0QiEQAAAAAAAAAA="), 32, atob("BgAAAAAAAAAAAAAAAAYCAAYGBgYGBgYGBgYCAAAAAAAABgYGBgYG"), 9 + (scale<<8));
g.drawString(timeStr, this.x+this.width/2, this.y+12);
}, redraw:function() {
var last = this.width;
if (!settingsChronowid.started) this.width = 0;
else this.width = (diff < 3600000) ? 58 : 48;
if (last != this.width) Bangle.drawWidgets();
else this.draw();
}, reload:function() {
settingsChronowid = require('Storage').readJSON("chronowid.json",1)||{};
if (interval) clearInterval(interval);
interval = undefined;
// start countdown each second
if (settingsChronowid.started) interval = setInterval(countDown, 1000);
// reset everything
countDown();
}};
//printDebug();
countDown();
})();
// set width correctly, start countdown each second
WIDGETS["chronowid"].reload();
})();

View File

@ -8,6 +8,8 @@
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)
0.08: Fix rendering of long messages (fix #969)
0.08: Fix rendering of long messages (fix #969)
buzz on new message (fix #999)
0.09: Respect the 'new' attribute if it was set from iOS integrations
0.09: Message now disappears after 60s if no action taken and clock loads (fix 922)
Fix phone icon (#1014)
0.10: Respect the 'new' attribute if it was set from iOS integrations

View File

@ -8,9 +8,17 @@ and responded to.
It is a replacement for the old `notify`/`gadgetbridge` apps.
## Usage
## Settings
You can change settings by going to the global `Settings` app, then `App Settings`
and `Messages`:
* `Vibrate` - This is the pattern of buzzes that should be made when a new message is received
* `Repeat` - How often should buzzes repeat - the default of 4 means the Bangle will buzz every 4 seconds
* `Unread Timer` - when a new message is received we go into the Messages app.
If there is no user input for this amount of time then the app will exit and return
to the clock where `MESSAGES` will be shown in the Widget bar.
...
## Requests

View File

@ -42,7 +42,12 @@ try {
};
}
/** this is a timeout if the app has started and is showing a single message
but the user hasn't seen it (eg no user input) - in which case
we should start a timeout for settings.unreadTimeout to return
to the clock. */
var unreadTimeout;
/// List of all our messages
var MESSAGES = require("Storage").readJSON("messages.json",1)||[];
if (!Array.isArray(MESSAGES)) MESSAGES=[];
var onMessagesModified = function(msg) {
@ -171,8 +176,14 @@ function showMessageSettings(msg) {
function showMessage(msgid) {
var msg = MESSAGES.find(m=>m.id==msgid);
if (!msg) return checkMessages(); // go home if no message found
if (msg.src=="Maps") return showMapMessage(msg);
if (msg.id=="music") return showMusicMessage(msg);
if (msg.src=="Maps") {
cancelReloadTimeout(); // don't auto-reload to clock now
return showMapMessage(msg);
}
if (msg.id=="music") {
cancelReloadTimeout(); // don't auto-reload to clock now
return showMusicMessage(msg);
}
// Normal text message display
var title=msg.title, titleFont = fontLarge, lines;
if (title) {
@ -186,14 +197,15 @@ function showMessage(msgid) {
}
var buttons = [
{type:"btn", src:getBackImage(), cb:()=>{
msg.new = false; // read mail
saveMessages();
msg.new = false; saveMessages(); // read mail
cancelReloadTimeout(); // don't auto-reload to clock now
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:1});
}} // back
];
if (msg.positive) {
buttons.push({type:"btn", src:getPosImage(), cb:()=>{
msg.new = false; saveMessages();
cancelReloadTimeout(); // don't auto-reload to clock now
Bangle.messageResponse(msg,true);
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1});
}});
@ -202,6 +214,7 @@ function showMessage(msgid) {
buttons.push({type:"btn", src:getNegImage(), cb:()=>{
console.log("Response");
msg.new = false; saveMessages();
cancelReloadTimeout(); // don't auto-reload to clock now
Bangle.messageResponse(msg,false);
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1});
}});
@ -210,7 +223,10 @@ function showMessage(msgid) {
var body = (lines.length>4) ? lines.slice(0,4).join("\n")+"..." : lines.join("\n");
layout = new Layout({ type:"v", c: [
{type:"h", fillx:1, bgCol:colBg, c: [
{ type:"btn", src:getMessageImage(msg), cb:()=>showMessageSettings(msg) },
{ type:"btn", src:getMessageImage(msg), cb:()=>{
cancelReloadTimeout(); // don't auto-reload to clock now
showMessageSettings(msg);
}},
{ type:"v", fillx:1, c: [
{type:"txt", font:fontSmall, label:msg.src||"Message", bgCol:colBg, fillx:1, pad:2, halign:1 },
title?{type:"txt", font:titleFont, label:title, bgCol:colBg, fillx:1, pad:2 }:{},
@ -249,7 +265,8 @@ function checkMessages(options) {
// no new messages - go to clock?
if (options.clockIfAllRead && newMessages.length==0)
return load();
// we don't have to time out of this screen...
cancelReloadTimeout();
// Otherwise show a menu
E.showScroller({
h : 48,
@ -291,9 +308,23 @@ function checkMessages(options) {
});
}
function cancelReloadTimeout() {
if (!unreadTimeout) return;
clearTimeout(unreadTimeout);
unreadTimeout = undefined;
}
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
setTimeout(() => {
var unreadTimeoutSecs = (require('Storage').readJSON("messages.settings.json", true) || {}).unreadTimeout;
if (unreadTimeoutSecs===undefined) unreadTimeoutSecs=60;
if (unreadTimeoutSecs)
unreadTimeout = setTimeout(function() {
print("Message not seen - reloading");
load();
}, unreadTimeoutSecs*1000);
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:1});
},10); // if checkMessages wants to 'load', do that

View File

@ -1,10 +1,10 @@
/* Push a new message onto messages queue, event is:
{t:"add",id:int, src,title,subject,body,sender,tel, important:bool, new:bool}
{t:"add",id:int, id:"music", state, artist, track, etc} // add new
{t:"remove-",id:int} // remove
{t:"modify",id:int, title:string} // modified
*/
exports.pushMessage = function(event) {
/* event is:
{t:"add",id:int, src,title,subject,body,sender,tel, important:bool, new:bool} // add new
{t:"add",id:int, id:"music", state, artist, track, etc} // add new
{t:"remove-",id:int} // remove
{t:"modify",id:int, title:string} // modified
*/
var messages, inApp = "undefined"!=typeof MESSAGES;
if (inApp)
messages = MESSAGES; // we're in an app that has already loaded messages
@ -51,6 +51,7 @@ exports.pushMessage = function(event) {
WIDGETS.messages.show();
}, 500);
}
/// Remove all messages
exports.clearAll = function(event) {
var messages, inApp = "undefined"!=typeof MESSAGES;
if (inApp) {

View File

@ -3,6 +3,7 @@
let settings = require('Storage').readJSON("messages.settings.json", true) || {};
if (settings.vibrate===undefined) settings.vibrate=".";
if (settings.repeat===undefined) settings.repeat=4;
if (settings.unreadTimeout===undefined) settings.unreadTimeout=60;
return settings;
}
function updateSetting(setting, value) {
@ -30,6 +31,12 @@
format: v => v+"s",
onchange: v => updateSetting("repeat", v)
},
'Unread timer': {
value: settings().unreadTimeout,
min: 0, max: 240, step : 10,
format: v => v?v+"s":"Off",
onchange: v => updateSetting("unreadTimeout", v)
},
};
E.showMenu(mainmenu);
})

View File

@ -1,4 +1,3 @@
WIDGETS["messages"]={area:"tl",width:0,draw:function() {
if (!this.width) return;
var c = (Date.now()-this.t)/1000;
@ -13,9 +12,10 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
WIDGETS["messages"].buzz(); // buzz every 4 seconds
}
setTimeout(()=>WIDGETS["messages"].draw(), 1000);
},show:function() {
},show:function(quiet) {
WIDGETS["messages"].t=Date.now(); // first time
WIDGETS["messages"].l=Date.now()-10000; // last buzz
if (quiet) WIDGETS["messages"].t -= 500000; // if quiet, set last time in the past so there is no buzzing
WIDGETS["messages"].width=64;
Bangle.drawWidgets();
Bangle.setLCDPower(1);// turns screen on
@ -34,3 +34,10 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
}
b();
}};
/* We might have returned here if we were in the Messages app for a
message but then the watch was never viewed. In that case we don't
want to buzz but should still show that there are unread messages. */
if (global.MESSAGES===undefined) (function() {
var messages = require("Storage").readJSON("messages.json",1)||[];
if (messages.some(m=>m.new)) WIDGETS["messages"].show(true);
})();

View File

@ -14,3 +14,4 @@
0.10: Add birthday style
0.11: Skip double buffering, use 240x240 size
0.12: Fix swipe direction (#800)
0.13: Bangle.js 2 support

View File

@ -0,0 +1,254 @@
// exec each function from seq one after the other
function animate(seq,period) {
var c = g.getColor();
var i = setInterval(function() {
if (seq.length) {
var f = seq.shift();
g.setColor(c);
if (f) f();
} else clearInterval(i);
},period);
}
// Fade in to FG color with angled lines
function fade(col, callback) {
var n = 0;
function f() {"ram"
g.setColor(col);
for (var i=n;i<240;i+=10) g.drawLine(i,0,0,i).drawLine(i,240,240,i);
g.flip();
n++;
if (n<10) setTimeout(f,0);
else callback();
}
f();
}
var SCENE_COUNT=11;
function getScene(n) {
if (n==0) return function() {
console.log("Start app");
g.clear(1);
eval(require("Storage").read("mywelcome.custom.js"));
}
if (n==1) return function() {
g.reset().setBgColor(0).clearRect(0,0,176,176);
g.setFont("6x15");
var n=0;
var l = Bangle.getLogo();
var im = g.imageMetrics(l);
var i = setInterval(function() {
n+=0.1;
g.setColor(n,n,n);
g.drawImage(l,(176-im.width)/2,(176-im.height)/2);
if (n>=1) {
clearInterval(i);
setTimeout(()=>g.drawString("Open",44,104), 500);
setTimeout(()=>g.drawString("Hackable",44,116), 1000);
setTimeout(()=>g.drawString("Smart Watch",44,128), 1500);
}
},50);
};
if (n==2) return function() {
var img = require("heatshrink").decompress(atob("ptR4n/j/4gH+8H5wl+jOukVVoHZ8dt/n//n37OtgH9sHhwHp4H5xmkGiH72MRje/LL/7iIAEE7sPEgoAC+AlagIlIiMQErPxDwUYxAABwIHCj8N7nOl3uEqa6BEggnFjfM5nCkUil3gEq5KDAAQmC6QmBE4JxSEhIABiQmB8QmSXoQlCYRMdEwIlCAAIlNhYlOiO85nNEyMPEoZwIAAcsYIYmPXoYlMiKaFExX/u9VEqLBBOYrCH+czmtVqJyDEpiaCOYsgSYszmc3qtTEqMR7hzG8AlGmd1OQglOOY6aEgYlCmmZoJMCTBrnD6SaIEoU/zOUuolSjbnBJgqaCEoU5zOXX4RyQYBBzCS4X5zNDqqZCJiERJg5zBEoVJEoM1JgYlQjhMHc4JLEmZMEEp6ZIJgPzS4WTmZMVTILmFYAK+BmglCmd1JgUYJiPNEorABEIOZygDBm5MCiJMQlhMH8ByBXwIlBJgUxJiMd5nOTIzlBTAK+BAANVq4jPAAS/HJgJyCTATAEACC/B4S/IJgIlCYAgAPiS/Kn5yEYANTEyPc5niOQxMB/LlCOapyJJgbpBYAZzROQK/Gl0ATIWfEoZzBc6IlB6SYGgBJBJgpzSlhyH8EAh5MBTIjnCuIlOjjlHTAJzC/LmDTSSYIEoTABOYIlETSKYHXwIABOYM0yYmETSCYHEobnDOYqaBExu8TAwlEc4U5EoiaCmK+NTAolFEwX0TQzBMXwXiEpTBCAAomNEoS+EEo4mIYIImKEoS+EEpDoBEyUbEo3gEo4mJdAImIJY4lJEycdEoPOOBYmPuIlE+HcJYhKKTZ1fhYkB2EAhnNcYMuEhomMr8A3YABEoJyB5gjOAAYmHm9VgELEoJMBEoXAEyXzE45YBJgXwEqx1I+ByDOYJyVJw5yCgEB3cQGgJMWJwQnCu6/CgFBigDB13S/glVAAf1qomCglEoADB1QDBADEPEoNVqEAolEgEKolKErJMDYAJMD0lE0AmaEoNaAgJMCFIYAahV/IgIiDOTgABNYJMEOToiCIoJMCOTzfCN4RMBOTxsDJIRyfIwZMBKQZzfJgRyfOYZMBOUBzCJgNKOT5zDJgLoCADxKBOAIABOT6aCAARyfOYRyjOYRyjOYlKEsBzEEsBzEOUJzDOUIABOUiaDOURzCOUZzCEscKCiY"));
var im = g.imageMetrics(img);
g.reset();
g.setBgColor("#ff00ff");
var y = 176, speed = 5;
function balloon(callback) {
y-=speed;
var x = (176-im.width)/2;
g.drawImage(img,x,y);
g.clearRect(x,y+81,x+77,y+81+speed);
if (y>30) setTimeout(balloon,0,callback);
else callback();
}
fade("#ff00ff", function() {
balloon(function() {
g.setColor(-1).setFont("6x15:2").setFontAlign(0,0);
g.drawString("Welcome.",88,130);
});
});
setTimeout(function() {
var n=0;
var i = setInterval(function() {
n+=4;
g.scroll(0,-4);
if (n>150)
clearInterval(i);
},20);
},3500);
};
if (n==3) return function() {
g.reset();
g.setBgColor("#ffff00").setColor(0).clear();
g.setFont("12x20").setFontAlign(0,0);
var x = 70, y = 25, h=25;
animate([
()=>g.drawString("Your",x,y+=h),
()=>g.drawString("Bangle.js",x,y+=h),
()=>g.drawString("has one",x,y+=h),
()=>g.drawString("button",x,y+=h),
()=>{g.setFont("12x20:2").setFontAlign(0,0,1).drawString("HERE!",150,88);}
],200);
};
if (n==4) return function() {
g.reset();
g.setBgColor("#00ffff").setColor(0).clear();
g.setFontAlign(0,0).setFont("6x15:2");
g.drawString("Press",88,40).setFontAlign(0,-1);
g.setFont("12x20");
g.drawString("To wake the\nscreen up, or to\nselect", 88,60);
};
if (n==5) return function() {
g.reset();
g.setBgColor("#00ffff").setColor(0).clear();
g.setFontAlign(0,0).setFont("6x15:2");
g.drawString("Long Press",88,40).setFontAlign(0,-1);
g.setFont("12x20");
g.drawString("To go back to\nthe clock", 88,60);
};
if (n==6) return function() {
g.reset();
g.setBgColor("#ff0000").setColor(0).clear();
g.setFontAlign(0,0).setFont("12x20");
g.drawString("If Bangle.js ever\nstops, hold the\nbutton for\nten seconds.\n\nBangle.js will\nthen reboot.", 88,78);
};
if (n==7) return function() {
g.reset();
g.setBgColor("#0000ff").setColor(-1).clear();
g.setFont("12x20").setFontAlign(0,0);
var x = 88, y = -20, h=60;
animate([
()=>{g.drawString("Bangle.js has a\nfull touchscreen",x,y+=h);},
0,0,
()=>{g.drawString("Drag up and down\nto scroll and\ntap to select",x,y+=h);},
],300);
};
if (n==8) return function() {
g.reset();
g.setBgColor("#00ff00").setColor(0).clear();
g.setFont("12x20").setFontAlign(0,0);
var x = 88, y = -35, h=80;
animate([
()=>{g.drawString("Bangle.js comes\nwith a few\napps installed",x,y+=h);},
0,0,
()=>{g.drawString("To add more, visit\nbanglejs.com/apps",x,y+=h);},
],400);
};
if (n==9) return function() {
g.reset();
g.setBgColor("#ff0000").setColor(0).clear();
g.setFont("12x20").setFontAlign(0,0);
var x = 88;
g.drawString("You can also make\nyour own apps!",x,30);
g.drawString("Check out\nbanglejs.com",x,130);
var rx = 0, ry = 0;
// draw a cube
function draw() {
// rotate
rx += 0.1;
ry += 0.11;
var rcx=Math.cos(rx),
rsx=Math.sin(rx),
rcy=Math.cos(ry),
rsy=Math.sin(ry);
// Project 3D coordinates into 2D
function p(x,y,z) {
var t;
t = x*rcy + z*rsy;
z = z*rcy - x*rsy;
x=t;
t = y*rcx + z*rsx;
z = z*rcx - y*rsx;
y=t;
z += 4;
return [88 + 60*x/z, 78+ 60*y/z];
}
var a;
// draw a series of lines to make up our cube
var s = 30;
g.clearRect(88-s,78-s,88+s,78+s);
a = p(-1,-1,-1); g.moveTo(a[0],a[1]);
a = p(1,-1,-1); g.lineTo(a[0],a[1]);
a = p(1,1,-1); g.lineTo(a[0],a[1]);
a = p(-1,1,-1); g.lineTo(a[0],a[1]);
a = p(-1,-1,-1); g.lineTo(a[0],a[1]);
a = p(-1,-1,1); g.moveTo(a[0],a[1]);
a = p(1,-1,1); g.lineTo(a[0],a[1]);
a = p(1,1,1); g.lineTo(a[0],a[1]);
a = p(-1,1,1); g.lineTo(a[0],a[1]);
a = p(-1,-1,1); g.lineTo(a[0],a[1]);
a = p(-1,-1,-1); g.moveTo(a[0],a[1]);
a = p(-1,-1,1); g.lineTo(a[0],a[1]);
a = p(1,-1,-1); g.moveTo(a[0],a[1]);
a = p(1,-1,1); g.lineTo(a[0],a[1]);
a = p(1,1,-1); g.moveTo(a[0],a[1]);
a = p(1,1,1); g.lineTo(a[0],a[1]);
a = p(-1,1,-1); g.moveTo(a[0],a[1]);
a = p(-1,1,1); g.lineTo(a[0],a[1]);
}
setInterval(draw,50);
};
if (n==10) return function() {
g.reset();
g.setBgColor("#ffffff");g.clear();
g.setFontAlign(0,0);
g.setFont("12x20");
var x = 88, y = 10, h=21;
animate([
()=>g.drawString("That's it!",x,y+=h),
()=>{g.drawString("Press",x,y+=h*2);
g.drawString("the button",x,y+=h);
g.drawString("to start",x,y+=h);
g.drawString("Bangle.js",x,y+=h);}
],400);
}
}
var sceneNumber = 0;
function move(dir) {
if (dir>0 && sceneNumber+1 == SCENE_COUNT) return; // at the end
sceneNumber = (sceneNumber+dir)%SCENE_COUNT;
if (sceneNumber<0) sceneNumber=0;
clearInterval();
getScene(sceneNumber)();
if (sceneNumber>1) {
var l = SCENE_COUNT;
for (var i=0;i<l-2;i++) {
var x = 88+(i-(l-2)/2)*12;
if (i<sceneNumber-1) {
g.setColor(-1).fillCircle(x,166,4);
} else {
g.setColor(0).fillCircle(x,166,4);
g.setColor(-1).drawCircle(x,166,4);
}
}
}
if (sceneNumber < SCENE_COUNT-1)
setTimeout(function() {
move(1);
}, (sceneNumber==0) ? 20000 : 5000);
}
Bangle.on('swipe', dir => move(dir));
setWatch(()=>{
if (sceneNumber == SCENE_COUNT-1)
load();
else
move(1);
}, BTN1, {repeat:true});
Bangle.setLCDTimeout(0);
Bangle.setLocked(0);
Bangle.setLCDPower(1);
move(0);

View File

@ -28,13 +28,15 @@ function getApp() {
var line3 = document.getElementById("line3").value;
var line4 = document.getElementById("line4").value;
var style = document.getElementById("style").value;
// build the app's text using a templated String
if (style=="Birthday") return `(function() {
var ib = require("heatshrink").decompress(atob("jk0ggGDhOZAAWQCYwMEBxAMFAAIaHyc/+c5DgwMC/84Dg4aCBgwcDBoOf+Y4GBoQEBn4zCI44DBDQ4NEyf4BpgoIBoefxINMBhApEBrQAKBrrrGWpANZHBT7FBpYqIFAYcJBggNOFQwoFDgwMHBwoMIBwYMKBrkykANLmcwBu0zBrMDBv4AFN5gA/ADY"));
var ir = require("heatshrink").decompress(atob("jk0ggGDhvdAAXQCYwMEBxAMFAAIaH6c/+c9DgwMC/8zDg4aC/4YCHIwNB7/zHAwNCAgM/DQwqDAYIaHBonT/oNMFBAND74NNBhApEBrQAKBrrrGWpANZHBT7FBpYqIFAYcJBgkA5oMF7gNFFQwoFDgwMHHIoMIAAPM5gMKBrk0oANLmcwBu0zBrMDBv4AFN5gA/ADYA="));
var ig = require("heatshrink").decompress(atob("jk0ggGDg93AAVwCYwMEBxAMFAAIaHuc/+c3DgwMC/8yDg4aC/4YCHIwNBv/zHAwNCAgM/DQwqDAYIaHBolz+4NMFBANDv8nBpgMIFIgNaABQNddYy1IBrI4KfYoNLFRAoDDhIMEgHnBgt+BooqGFAoqGBg4OFBhAODBhQNcmUgBpczmAN2mYNZgYN/AApvMAH4Ab"));
var igift = require("heatshrink").decompress(atob("q1QxH+ADOi0QbZ5nMHDQAbKgIACKa4ACKnJWVKghW0KgxWTKgxWyKhBWRKhBWwKhRWPKhRWuKhhWNKhhWtKpxWKKhys8KxBU8Ky5U+KypU/KyhU/KyhU/KynGKn5WTKn5WUKmHCADpJJE7uYABZUfKuuYKv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/AAv+Kv5VT/wADyIAaKpIlbABZSEKv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/Kv5V/ADNtKv6rdKzZVwKhAABy5V/Khw"));
var W=240,H=240;
var W=g.getWidth(),H=g.getHeight();
var titleFont = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
var blns = [];
function updateFlake(f) {
f.im = [ir,ig,ib][Math.round(Math.random()*100)%3];
@ -60,7 +62,7 @@ var ig = require("heatshrink").decompress(atob("jk0ggGDg93AAVwCYwMEBxAMFAAIaHuc/
});
var x = W/2, y = H/2;
g.drawImage(igift,x-43,y-80);
g.setFont("6x8",2).setFontAlign(0,0);
g.setFont(titleFont).setFontAlign(0,0);
g.drawString(${JSON.stringify(line1)},x,y+=20);
g.drawString(${JSON.stringify(line2)},x,y+=20);
g.setFont("6x8");
@ -68,7 +70,7 @@ var ig = require("heatshrink").decompress(atob("jk0ggGDg93AAVwCYwMEBxAMFAAIaHuc/
g.drawString(${JSON.stringify(line4)},x,y+=10);
g.flip();
}
g.clear();
g.clear(1).setBgColor(0).setColor(-1).clearRect(0,0,W,H);
setInterval(draw,50);
})()`;
// if (style=="Christmas")
@ -76,6 +78,7 @@ var ig = require("heatshrink").decompress(atob("jk0ggGDg93AAVwCYwMEBxAMFAAIaHuc/
var isnow = require("heatshrink").decompress(atob("jEagQWTgfAAocf+gFDh4FDiARBggVB3AFBl3Agf8jfkn/AgX/v/9/+Agfv/2//YrBgfwh4wCgfghYFJCIYdFFIw1EIIpNFL44FFOIoAP"));
var itree = require("heatshrink").decompress(atob("mtWxH+ADHHDTI0aGuXH5vNGmhqvTYIzBGtoxF6fTG4g4oGgQyBAAZssGoI0Ga1g1FGdo01ZgIAEGmHHNoLSuAAN/rdb0YFBGlgCBGYIABA4YArGYY1CGn4znAAM6GeVd5PQ5Iyurc/vQ0oGZFAn+d4XC3d5GddiGYIEBy+7zoEBGlFhoEcsQ9GT08+oFk1mkGdaVBMgNArnJ6/KzswGs/J6GlrlbqtbvPC5PCy8wGohniMIPJvIpCqmX3e7vI0BqhqlMIY0DqhtBqoEBa0xgBMIIoEqoABGQwzfsIhBv4qHABM50vQGjg1CGaN66DoBGt1ioGd5LoBGjo1PGYNhvLoCa7wnBqgvGA4YzCAgN5GUAsCqoDBmAHCAYU/wPQ0oSDGcBiDqkwAYcxoFd5PX6GdGjrIIqtUAAc3jk5vPC4fCy5pef5I2BTQMcnAHBy+7y95T0oADnFk1ekBpI2aGRUin7NGAA9hsIzVsIgHTAKZBZoPJ5LNDGhBpXGolcwOsrtcA4TNB3bNDGb/+sVin9AoGe6HX5InEvN/TkP+5XQwM/sRsBzqWB4QuKGjvC6HQ4QdDvKWBZYMwmAuHmFUCYNbqibX3fD5O7qolEZQQ0FBwgKDqgJBGiphEDwNUEgJbBFIQqCAgYOCB4IzCnE6GyhYFGoQnDABYzGAAQ1UAAo2NBoQSBnOB0t/Gjo2EABIPCoGe6HX4QzTGRIAEqtVF4QEBBQc4oE4y/J5PCvIxeABk/oADBvO73eXTyAyZMwM/Awd5vIOFGslAr2Av4PLNcU/jmA6HX5I1KasFcn8dTIOd5PJ4SZGGiNhAAIyNn0ckU+ZYe7AAJpJEYJnNGZk+n9kw9cBAcwGoN5aZg1JJJQABm8/oEjoDKC5ALCrUwqh/NrvQ6HDGp04n9doEdoE/sQJBZQZhCqgABGZk6zw0K/1dnVAoNAFwOlCYL1FubJBy4GCGh1AnOX4XC3YzHFYOeCgdV5PQ5OdD4rKBqqYNGYlbv+X3edGY3CGgKMDAAO7JAJgDAClcr2BEYgADaIZ0DL4uXGbDuB6HX5I1GsP+sNhOgWXIhBmWd4Od5PK4TwFGIJoBAYI2BAD0/jlcQoO7AAJaEGQQADGr0/sjNEvOdAoZmDGgw2ZsVAkeAZpQACGZI2VsU/kVGn1bZoPJZogpGGhA4GfRYwBoGC1mlBQbNFFoo0JNxAGCEod/wM6oFAn9iv/J6/Kzo1Ey9/MZQAKCg4GCFgTDEvPCSwI0BC5I0RN4ocEYYPQ5OdHgeXSwTFKGaJyKFYPC3f+MIdbpzFLAD4zB/1OqtbqtOGgYArGAIADGl9UAAI0wGQN5GoQ0vvIABGoI0uGYQABqo0zNOg0uaQY0/GllOGn40//w="));
var W=g.getWidth(),H=g.getHeight();
var titleFont = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
var flakes = [];
for (var i=0;i<10;i++) {
var f = {
@ -97,7 +100,7 @@ var ig = require("heatshrink").decompress(atob("jk0ggGDg93AAVwCYwMEBxAMFAAIaHuc/
});
var x = W/2, y = H/2;
g.drawImage(itree,x-27,y-80);
g.setFont("6x8",2).setFontAlign(0,0);
g.setFont(titleFont).setFontAlign(0,0);
g.drawString(${JSON.stringify(line1)},x,y+=20);
g.drawString(${JSON.stringify(line2)},x,y+=20);
g.setFont("6x8");
@ -105,7 +108,7 @@ var ig = require("heatshrink").decompress(atob("jk0ggGDg93AAVwCYwMEBxAMFAAIaHuc/
g.drawString(${JSON.stringify(line4)},x,y+=10);
g.flip();
}
g.clear();
g.clear(1).setBgColor(0).setColor(-1).clearRect(0,0,W,H);
setInterval(draw,50);
})();
`;

View File

@ -8,3 +8,5 @@
0.08: Update for drag event refactor
0.09: Use current theme cols when drawing GPS info
0.10: Improve scale factor calculation to fix scaling issues (#984)
0.11: Add slight offset to OSM data to align it properly (fix #984)
Fix alignment of satellite info text

View File

@ -25,11 +25,11 @@ function drawMarker() {
var fix;
Bangle.on('GPS',function(f) {
fix=f;
g.reset().clearRect(0,y1,240,y1+8).setFont("6x8").setFontAlign(0,0);
g.reset().clearRect(0,y1,g.getWidth()-1,y1+8).setFont("6x8").setFontAlign(0,0);
var txt = fix.satellites+" satellites";
if (!fix.fix)
txt += " - NO FIX";
g.drawString(txt,120,y1 + 4);
g.drawString(txt,g.getWidth()/2,y1 + 4);
drawMarker();
});
Bangle.setGPSPower(1, "app");

View File

@ -132,8 +132,10 @@ TODO:
var zoom = map.getZoom();
var centerlatlon = map.getBounds().getCenter();
var center = map.project(centerlatlon, zoom).divideBy(OSMTILESIZE);
var ox = Math.round((center.x - Math.floor(center.x)) * OSMTILESIZE);
var oy = Math.round((center.y - Math.floor(center.y)) * OSMTILESIZE);
// Reason for 16px adjustment below not 100% known, but it seems to
// align everything perfectly: https://github.com/espruino/BangleApps/issues/984
var ox = Math.round((center.x - Math.floor(center.x)) * OSMTILESIZE) + 16;
var oy = Math.round((center.y - Math.floor(center.y)) * OSMTILESIZE) + 16;
center = center.floor(); // make sure we're in the middle of a tile
// JS version of Bangle.js's projection
function bproject(lat, lon) {
@ -155,8 +157,15 @@ TODO:
var bd = bproject(pd.lat, pd.lng)
var scale = bc.distanceTo(bd);
var tileGetters = [];
// test
/*var p = bproject(centerlatlon.lat, centerlatlon.lng);
var q = bproject(mylat, mylon);
var testPt = {
x : (q.x-p.x)/scale + (MAPSIZE/2),
y : (MAPSIZE/2) - (q.y-p.y)/scale
};*/
var tileGetters = [];
// Render everything to a canvas...
var canvas = document.getElementById("maptiles");
canvas.style.display="";
@ -173,6 +182,11 @@ TODO:
tileGetters.push(new Promise(function(resolve,reject) {
img.onload = function(){
ctx.drawImage(img,i*OSMTILESIZE - ox, j*OSMTILESIZE - oy);
/*if (testPt) {
ctx.fillStyle="green";
ctx.fillRect(testPt.x-1, testPt.y-5, 3,10);
ctx.fillRect(testPt.x-5, testPt.y-1, 10,3);
}*/
resolve();
};
}));

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -37,3 +37,4 @@
0.32: Fix 'beep' menu on Bangle.js 2
0.33: Really fix 'beep' menu on Bangle.js 2 this time
0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app
0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen

View File

@ -96,7 +96,7 @@ function showMainMenu() {
'': { 'title': 'Settings' },
'< Back': ()=>load(),
'Make Connectable': ()=>makeConnectable(),
'App/Widget Settings': ()=>showAppSettingsMenu(),
'App Settings': ()=>showAppSettingsMenu(),
'BLE': ()=>showBLEMenu(),
'Debug Info': {
value: settings.log,

View File

@ -1 +1,2 @@
0.01: New App!
0.01: New App!
0.02: Minor layout format tweak so it uses less memory and draws ok on Bangle.js 1 (#1012)

View File

@ -69,22 +69,20 @@ var clockLayout = new Layout( {
{type:"txt", font:"10%", label:"01/01/1970", id:"date" }
]
},
{type: "v", fillx: 1, c: [
{type: "h", valign : 1, fillx:1, c: [
{type: "img", filly: 1, id: "weatherIcon", src: sunIcon},
{type: "v", fillx:1, c: [
{type: "h", c: [
{type: "txt", font: "10%", id: "temp", label: "000"},
{type: "txt", font: "10%", id: "tempUnit", label: "°C"},
]},
{type: "h", c: [
{type: "txt", font: "10%", id: "wind", label: "00"},
{type: "txt", font: "10%", id: "windUnit", label: "km/h"},
]}
]
},
]},
]}]
{type: "h", valign : 1, fillx:1, c: [
{type: "img", filly: 1, id: "weatherIcon", src: sunIcon},
{type: "v", fillx:1, c: [
{type: "h", c: [
{type: "txt", font: "10%", id: "temp", label: "000"},
{type: "txt", font: "10%", id: "tempUnit", label: "°C"},
]},
{type: "h", c: [
{type: "txt", font: "10%", id: "wind", label: "00"},
{type: "txt", font: "10%", id: "windUnit", label: "km/h"},
]}
]
},
]}]
});
// timeout used to update every minute
@ -133,4 +131,4 @@ Bangle.setUI("clock"); // Show launcher when middle button pressed
Bangle.loadWidgets();
Bangle.drawWidgets();
clockLayout.render();
draw();
draw();

View File

@ -244,5 +244,6 @@ setWatch(()=>{
}, BTN1, {repeat:true});
Bangle.setLCDTimeout(0);
Bangle.setLocked(0);
Bangle.setLCDPower(1);
move(0);