pull/1656/head
marko 2022-04-05 09:15:45 -04:00
commit 92bd3d5df9
58 changed files with 645 additions and 78 deletions

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"aclock.app.js","url":"clock-analog.js"},

View File

@ -9,3 +9,4 @@
when weekday name and calendar weeknumber are on then display is <weekday short> #<calweek>
week is buffered until date or timezone changes
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
0.08: fixed calendar weeknumber not shortened to two digits

View File

@ -99,7 +99,7 @@ function updateState() {
}
function isoStr(date) {
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).substr(-2) + "-" + ("0" + date.getDate()).substr(-2);
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2);
}
var calWeekBuffer = [false,false,false]; //buffer tz, date, week no (once calculated until other tz or date is requested)
@ -140,7 +140,7 @@ function draw() {
g.setFontAlign(0, 0).setFont("Anton").drawString(timeStr, x, y); // draw time
if (secondsScreen) {
y += 65;
var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).substr(-2);
var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).slice(-2);
if (doColor())
g.setColor(0, 0, 1);
g.setFont("AntonSmall");
@ -193,7 +193,7 @@ function draw() {
if (calWeek || weekDay) {
var dowcwStr = "";
if (calWeek)
dowcwStr = " #" + ("0" + ISO8601calWeek(date)).substring(-2);
dowcwStr = " #" + ("0" + ISO8601calWeek(date)).slice(-2);
if (weekDay)
dowcwStr = require("locale").dow(date, calWeek ? 1 : 0) + dowcwStr; //weekDay e.g. Monday or weekDayShort #<calWeek> e.g. Mon #01
else //week #01

View File

@ -1,7 +1,7 @@
{
"id": "antonclk",
"name": "Anton Clock",
"version": "0.07",
"version": "0.08",
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
"readme":"README.md",
"icon": "app.png",

View File

@ -7,6 +7,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"screenshots": [{"url":"berlin-clock-screenshot.png"}],
"storage": [

2
apps/bordle/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App
0.02: app keeps track of statistics now

View File

@ -2,7 +2,7 @@
"name": "Bordle",
"shortName":"Bordle",
"icon": "app.png",
"version":"0.01",
"version":"0.02",
"description": "Bangle version of a popular word search game",
"supports" : ["BANGLEJS2"],
"readme": "README.md",

View File

@ -8,6 +8,7 @@
"screenshots": [{"url":"screenshot_calculator.png"}],
"tags": "app,tool",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"calculator.app.js","url":"app.js"},
{"name":"calculator.img","url":"calculator-icon.js","evaluate":true}

View File

@ -9,6 +9,7 @@
"type": "clock",
"tags": "clock,cli,command,bash,shell",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"cliock.app.js","url":"app.js"},

View File

@ -3,3 +3,4 @@
0.03: fix metadata.json to allow setting as clock
0.04: added heart rate which is switched on when cycled to it through up/down touch on rhs
0.05: changed text to uppercase, just looks better, removed colons on text
0.06: better contrast for light theme, use fg color instead of dithered for ring

View File

@ -28,5 +28,6 @@ See [#1248](https://github.com/espruino/BangleApps/issues/1248)
## Screenshots
![](screenshot_daisy1.png)
![](screenshot_daisy3.png)
It is worth looking at the real thing though as the screenshot does not do it justice.
It is worth looking at the real thing though as the screenshots do not do it justice.

View File

@ -41,10 +41,17 @@ Graphics.prototype.setFontRoboto20 = function(scale) {
};
function assignPalettes() {
if (g.theme.dark) {
// palette for 0-40%
pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]);
// palette for 50-100%
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]);
} else {
// palette for 0-40%
pal1 = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor(settings.fg), g.toColor("#00f")]);
// palette for 50-100%
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.theme.fg, g.toColor("#00f")]);
}
}
function setSmallFont20() {
@ -109,10 +116,10 @@ function updateSunRiseSunSet(now, lat, lon, line){
const infoData = {
ID_DATE: { calc: () => {var d = (new Date()).toString().split(" "); return d[2] + ' ' + d[1] + ' ' + d[3];} },
ID_DAY: { calc: () => {var d = require("locale").dow(new Date()).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} },
ID_SR: { calc: () => 'Sunrise ' + sunRise },
ID_SS: { calc: () => 'Sunset ' + sunSet },
ID_STEP: { calc: () => 'Steps ' + getSteps() },
ID_BATT: { calc: () => 'Battery ' + E.getBattery() + '%' },
ID_SR: { calc: () => 'SUNRISE ' + sunRise },
ID_SS: { calc: () => 'SUNSET ' + sunSet },
ID_STEP: { calc: () => 'STEPS ' + getSteps() },
ID_BATT: { calc: () => 'BATTERY ' + E.getBattery() + '%' },
ID_HRM: { calc: () => hrmCurrent }
};
@ -225,7 +232,7 @@ function drawSteps() {
setSmallFont();
g.setFontAlign(0,0);
g.setColor(g.theme.fg);
g.drawString('Steps ' + getSteps(), w/2, (3*h/4) - 4);
g.drawString('STEPS ' + getSteps(), w/2, (3*h/4) - 4);
drawingSteps = false;
}

View File

@ -1,13 +1,13 @@
{ "id": "daisy",
"name": "Daisy",
"version":"0.05",
"version":"0.06",
"dependencies": {"mylocation":"app"},
"description": "A clock based on the Pastel clock with large ring guage for steps",
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
"icon": "app.png",
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS2"],
"screenshots": [{"url":"screenshot_daisy2.jpg"}],
"screenshots": [{"url":"screenshot_daisy3.png"}],
"readme": "README.md",
"storage": [
{"name":"daisy.app.js","url":"app.js"},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"ffcniftyb.app.js","url":"app.js"},

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"floralclk.app.js","url":"app.js"},

View File

@ -6,3 +6,4 @@
0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw
0.07: Optimized the mover algorithm for efficiency (work in progress)
0.08: Bug fix at end of the game with victorious splash and glorious orchestra
0.09: Added settings menu, removed symbol selection button (*), added highscore reset

View File

@ -1,7 +1,7 @@
# Play the game of 1024
Move the tiles by swiping to the lefthand, righthand or up- and downward side of the watch.
Move the tiles by swiping left, right, up- or downward over the watchface.
When two tiles with the same number are squashed together they will add up as exponentials:
@ -21,16 +21,28 @@ Use the side **BTN** to exit the game, score and tile positions will be saved.
## Buttons on the screen
- Button **U**: Undo the last move. There are currently a maximum of 4 undo levels. The level is indicated with a small number in the lower righthand corner of the Undo button
- Button **\***: Change the text on the tile to number, capitals or Roman numbers
- Button **R**: Reset the game. The Higscore will be remembered. You will be prompted first.
- Button **U**: Undo the last move. There are currently a maximum of 9 undo levels. The level is indicated with a small number in the lower righthand corner of the Undo button
- You can set the maximum undo level in the Apps settings menu.
- Button **R**: Reset the game. The Highscore will be remembered. You will be prompted first.
- The highscore value can be reset in the Apps settings menu.
Apps setting: ![Screenshot of the apps settings menu](./game1024_sc_dump_app_settings.png)
- Stuff you can change in de 1024 Game settings:
- Symbols on the cells: numerical, alphabetical or Roman
- Undo levels [0-9]
- Exit: how to exit the game: long or short press
- Debug mode: on or off. This will log all kinds of stuff in the console of the Web IDE
- Reset Highsccore: Tired of looking at the old highscore? Now you can set it to 0 again.
### Credits
Game 1024 is based on Saming's 2048 and Misho M. Petkovic 1024game.org and conceptually similar to Threes by Asher Vollmer.
In Dark theme with numbers:
![Screenshot from the Banglejs 2 watch with the game in dark theme](./game1024_sc_dump_dark.png)
![Screenshot from the Banglejs 2 watch with the game in dark theme](./game1024_sc_dump_dark_v0.09.png)
In Light theme with characters:
![Screenshot from the Banglejs 2 watch with the game in light theme](./game1024_sc_dump_light.png)
![Screenshot from the Banglejs 2 watch with the game in light theme](./game1024_sc_dump_light.v0.09.png)

View File

@ -1,6 +1,20 @@
const debugMode = 'off'; // valid values are: off, test, production, development
let settings = Object.assign({
// default values
maxUndoLevels: 4,
charIndex: 0,
clockMode: true,
debugMode: false,
}, require('Storage').readJSON("game1024.settings.json", true) || {});
const clockMode = settings.clockMode!==undefined ? settings.clockMode : true;
const debugMode = settings.debugMode!==undefined ? settings.debugMode : false; // #settings -- valid values are: true or false
const maxUndoLevels = settings.maxUndoLevels!==undefined ? settings.maxUndoLevels : 4; // #settings
const charIndex = settings.charIndex!==undefined ? settings.charIndex : 0; // #settings -- plain numbers on the grid
delete settings; // remove unneeded settings from memory
const middle = {x:Math.floor(g.getWidth()/2)-20, y: Math.floor(g.getHeight()/2)};
const rows = 4, cols = 4;
const rows = 4, cols = 4; // #settings
const borderWidth = 6;
const sqWidth = (Math.floor(Bangle.appRect.w - 48) / rows) - borderWidth;
const cellColors = [{bg:'#00FFFF', fg: '#000000'},
@ -13,12 +27,8 @@ const cellChars = [
['0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
['0','I', 'II', 'III', 'IV', 'V', 'VI', 'VII','VIII', 'IX', 'X']
];
// const numInitialCells = 2;
const maxUndoLevels = 4;
const noExceptions = true;
let charIndex = 0; // plain numbers on the grid
const themeBg = g.theme.bg;
const themeBg = g.theme.bg;
const scores = {
currentScore: 0,
@ -78,12 +88,12 @@ const snapshot = {
updCounter: function() {
this.counter = ++this.counter > this.interval ? 0 : this.counter;
},
dump: {gridsize: rows * cols, expVals: [], score: 0, highScore: 0, charIndex: charIndex},
dump: {gridsize: rows * cols, expVals: [], score: 0, highScore: 0},
write: function() {
require("Storage").writeJSON(this.snFileName, this.dump);
},
read: function () {
let sn = require("Storage").readJSON(this.snFileName, noExceptions);
let sn = require("Storage").readJSON(this.snFileName, true);
if ((typeof sn == "undefined") || (sn.gridsize !== rows * cols)) {
require("Storage").writeJSON(this.snFileName, this.dump);
return false;
@ -101,7 +111,6 @@ const snapshot = {
});
this.dump.score = scores.currentScore;
this.dump.highScore = scores.highScore;
this.dump.charIndex = charIndex;
},
make: function () {
this.updCounter();
@ -118,7 +127,7 @@ const snapshot = {
});
scores.currentScore = this.dump.score ? this.dump.score : 0;
scores.highScore = this.dump.highScore ? this.dump.highScore : 0 ;
charIndex = this.dump.charIndex ? this.dump.charIndex : 0 ;
if (this.dump.hasOwnProperty('charIndex')) delete this.dump.charIndex; // depricated in v0.09
}
},
reset: function () {
@ -129,12 +138,11 @@ const snapshot = {
}
this.dump.score = 0;
this.dump.highScore = scores.highScore;
this.dump.charIndex = charIndex;
this.write();
debug(() => console.log("reset D U M P E D!", this.dump));
}
};
const btnAtribs = {x: 134, w: 42, h: 42, fg:'#C0C0C0', bg:'#800000'};
const btnAtribs = {x: 134, w: 42, h: 50, fg:'#C0C0C0', bg:'#800000'};
const buttons = {
all: [],
draw: function () {
@ -314,7 +322,7 @@ class Cell {
}
drawBg() {
debug(()=>console.log("Drawbg!!"));
if (this.isRndm == true) {
if (this.isRndm) {
debug(()=>console.log('Random: (ax)', this.ax));
g.setColor(this.getColor(this.expVal).bg)
.fillRect(this.x0, this.y0, this.x1, this.y1)
@ -365,7 +373,7 @@ class Cell {
this.isRndm = true;
}
drawRndmIndicator(){
if (this.isRndm == true) {
if (this.isRndm) {
debug(()=>console.log('Random: (ax)', this.ax));
g.setColor(this.getColor(0).bg)
.fillPoly(this.ax,this.ay,this.bx,this.by,this.cx,this.cy);
@ -374,8 +382,9 @@ class Cell {
}
function undoGame() {
if (scores.lastScores.length) {
g.clear();
if (scores.lastScores.length > 0) {
allSquares.forEach(sq => {
sq.popFromUndo();
sq.drawBg();
@ -386,10 +395,10 @@ function undoGame() {
buttons.draw();
updUndoLvlIndex();
snapshot.make();
}
Bangle.loadWidgets();
Bangle.drawWidgets();
}
}
function addToUndo() {
allSquares.forEach(sq => {
sq.pushToUndo();
@ -487,8 +496,8 @@ function initGame() {
drawGrid();
scores.draw();
buttons.draw();
// Clock mode allows short-press on button to exit
Bangle.setUI("clock");
// #settings Clock mode allows short-press on button to exit
if(clockMode) Bangle.setUI("clock");
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
@ -507,8 +516,8 @@ function drawPopUp(message,cb) {
rDims.x+10, rDims.y2-40
]);
buttons.all.forEach(btn => {btn.disable();});
const btnYes = new Button('yes', rDims.x+16, rDims.y2-80, 54, btnAtribs.h, 'YES', btnAtribs.fg, btnAtribs.bg, cb, true);
const btnNo = new Button('no', rDims.x2-80, rDims.y2-80, 54, btnAtribs.h, 'NO', btnAtribs.fg, btnAtribs.bg, cb, true);
const btnYes = new Button('yes', rDims.x+16, rDims.y2-88, 54, btnAtribs.h, 'YES', btnAtribs.fg, btnAtribs.bg, cb, true);
const btnNo = new Button('no', rDims.x2-80, rDims.y2-88, 54, btnAtribs.h, 'NO', btnAtribs.fg, btnAtribs.bg, cb, true);
btnYes.draw();
btnNo.draw();
g.setColor('#000000');
@ -560,14 +569,8 @@ function resetGame() {
* @param {function} func function to call like console.log()
*/
const debug = (func) => {
switch (debugMode) {
case "development":
if (typeof func === 'function') {
func();
}
break;
case "off":
default: break;
if (debugMode) {
if (typeof func === 'function') func();
}
};
@ -690,13 +693,9 @@ function updUndoLvlIndex() {
.drawString(scores.lastScores.length, x, y);
}
}
function incrCharIndex() {
charIndex++;
if (charIndex >= cellChars.length) charIndex = 0;
drawGrid();
}
buttons.add(new Button('undo', btnAtribs.x, 25, btnAtribs.w, btnAtribs.h, 'U', btnAtribs.fg, btnAtribs.bg, undoGame, true));
buttons.add(new Button('chars', btnAtribs.x, 71, btnAtribs.w, 31, '*', btnAtribs.fg, btnAtribs.bg, function(){incrCharIndex();}, true));
buttons.add(new Button('restart', btnAtribs.x, 106, btnAtribs.w, btnAtribs.h, 'R', btnAtribs.fg, btnAtribs.bg, function(){drawPopUp('Do you want\nto restart?',handlePopUpClicks);}, true));
initGame();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,7 +1,7 @@
{ "id": "game1024",
"name": "1024 Game",
"shortName" : "1024 Game",
"version": "0.08",
"version": "0.09",
"icon": "game1024.png",
"screenshots": [ {"url":"screenshot.png" } ],
"readme":"README.md",
@ -12,6 +12,7 @@
"supports" : ["BANGLEJS2"],
"storage": [
{"name":"game1024.app.js","url":"app.js"},
{"name":"game1024.settings.js","url":"settings.js"},
{"name":"game1024.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

70
apps/game1024/settings.js Normal file
View File

@ -0,0 +1,70 @@
(function(back) {
var FILE = "game1024.settings.json";
var scoreFile = "game1024.json";
// Load settings
var settings = Object.assign({
maxUndoLevels: 5,
charIndex: 0,
clockMode: true,
debugMode: false,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
var symbols = ["1 2 3 ...", "A B C ...", "I II III..."];
var settingsMenu = {
"" : { "title" : "1024 Game" },
"< Back" : () => back(),
"Symbols": {
value: 0|settings.charIndex,
min:0,max:symbols.length-1,
format: v=>symbols[v],
onchange: v=> { settings.charIndex=v; writeSettings();}
}
,
"Undo levels:": {
value: 0|settings.maxUndoLevels, // 0| converts undefined to 0
min: 0, max: 9,
onchange: v => {
settings.maxUndoLevels = v;
writeSettings();
}
},
"Exit press:": {
value: !settings.debugMode, // ! converts undefined to true
format: v => v?"short":"long",
onchange: v => {
settings.debugMode = v;
writeSettings();
},
},
"Debug mode:": {
value: !!settings.debugMode, // !! converts undefined to false
format: v => v?"On":"Off",
onchange: v => {
settings.debugMode = v;
writeSettings();
}
},
"Reset Highscore": () => {
E.showPrompt('Reset Highscore?').then((v) => {
let delay = 50;
if (v) {
delay = 500;
let sF = require("Storage").readJSON(scoreFile, true);
if (typeof sF !== "undefined") {
E.showMessage('Resetting');
sF.highScore = 0;
require("Storage").writeJSON(scoreFile, sF);
} else {
E.showMessage('No highscore!');
}
}
setTimeout(() => E.showMenu(settingsMenu), delay);
});
}
}
// Show the menu
E.showMenu(settingsMenu);
})

View File

@ -8,6 +8,7 @@
"tags": "clock",
"screenshots": [{"url":"bangle1-high-contrast-clock-screenshot.png"}],
"supports": ["BANGLEJS"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"hcclock.app.js","url":"hcclock.app.js"},

View File

@ -7,6 +7,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}],
"allow_emulator": true,
"storage": [

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"intclock.app.js","url":"app.js"},

View File

@ -7,6 +7,7 @@
"icon": "intervals.png",
"tags": "",
"supports": ["BANGLEJS"],
"readme": "README.md",
"storage": [
{"name":"intervals.app.js","url":"intervals.app.js"},
{"name":"intervals.img","url":"intervals-icon.js","evaluate":true}

View File

@ -7,6 +7,7 @@
"tags": "tool,system,ios,apple,messages,notifications",
"dependencies": {"messages":"app"},
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"ios.app.js","url":"app.js"},
{"name":"ios.img","url":"app-icon.js","evaluate":true},

View File

@ -42,3 +42,4 @@
0.27: Add 'mark all read' option to popup menu (fix #1624)
0.28: Option to auto-unlock the watch when a new message arrives
0.29: Fix message list overwrites on Bangle.js 1 (fix #1642)
0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel)

View File

@ -82,31 +82,45 @@ function getNegImage() {
return atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA=");
}
/*
* icons should be 24x24px with 1bpp colors and transparancy
* icons should be 24x24px with 1bpp colors and 'Transparency to Color'
* http://www.espruino.com/Image+Converter
*/
function getMessageImage(msg) {
if (msg.img) return atob(msg.img);
var s = (msg.src||"").toLowerCase();
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA");
if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA");
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA");
if (s=="facebook") return getFBIcon();
if (s=="gmail") return getNotificationImage();
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA=");
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA==");
if (s=="gmail") return getNotificationImage();
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44");
if (s=="mail") return getNotificationImage();
if (s=="messenger") return getFBIcon();
if (s=="outlook mail") return getNotificationImage();
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA=");
if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA=");
if (s=="post & dhl") return atob("GBgBAPgAE/5wMwZ8NgN8NgP4NgP4HgP4HgPwDwfgD//AB/+AAf8AAAAABs7AHcdgG4MwAAAAGESAFESAEkSAEnyAEkSAFESAGETw");
if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA");
if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA==");
if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA==");
if (s=="sms message") return getNotificationImage();
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA");
if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA");
if (s=="telegram") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
if (s=="to do") return atob("GBgBAAAAAAAAAAAwAAB4AAD8AAH+AAP/DAf/Hg//Px/+f7/8///4///wf//gP//AH/+AD/8AB/4AA/wAAfgAAPAAAGAAAAAAAAAA");
if (s=="twitch") return atob("GBgBH//+P//+P//+eAAGeAAGeAAGeDGGeDOGeDOGeDOGeDOGeDOGeDOGeAAOeAAOeAAcf4/4f5/wf7/gf//Af/+AA/AAA+AAAcAA");
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA==");
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
if (s=="youtube") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
if (msg.id=="back") return getBackImage();
return getNotificationImage();
@ -115,28 +129,38 @@ function getMessageImageCol(msg,def) {
return {
// generic colors, using B2-safe colors
"alarm": "#fff",
"calendar": "#f00",
"mail": "#ff0",
"music": "#f0f",
"phone": "#0f0",
"sms message": "#0ff",
// brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos)
// all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?)
"bibel": "#54342c",
"discord": "#738adb",
"facebook": "#4267b2",
"gmail": "#ea4335",
"google home": "#fbbc05",
"home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
"hangouts": "#1ba261",
"home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
"instagram": "#dd2a7b",
"liferando": "#ee5c00",
"messenger": "#0078ff",
"nina": "#e57004",
"outlook mail": "#0072c6",
"post & dhl": "#f2c101",
"signal": "#00f",
"skype": "#00aff0",
"slack": "#e51670",
"threema": "#000",
"snapchat": "#ff0",
"teams": "#464eb8",
"telegram": "#0088cc",
"threema": "#000",
"to do": "#3999e5",
"twitch": "#6441A4",
"twitter": "#1da1f2",
"whatsapp": "#4fce5d",
"wordfeud": "#e7d3c7",
"youtube": "#f00",
}[(msg.src||"").toLowerCase()]||(def !== undefined?def:g.theme.fg);
}

View File

@ -1,7 +1,7 @@
{
"id": "messages",
"name": "Messages",
"version": "0.29",
"version": "0.30",
"description": "App to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "numerals,clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"screenshots": [{"url":"bangle1-numerals-screenshot.png"}],
"storage": [

View File

@ -7,6 +7,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS"],
"readme": "README.md",
"allow_emulator": true,
"screenshots": [{"url":"bangle1-pipboy-themed-clock-screenshot.png"}],
"storage": [

View File

@ -7,6 +7,7 @@
"type": "boot",
"tags": "system",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"screenshots": [{"url":"pro-menu-screenshot.png"}],
"storage": [
{"name":"promenu.boot.js","url":"boot.js","supports": ["BANGLEJS"]},

View File

@ -14,7 +14,8 @@ the red `STOP` in the bottom right turns to a green `RUN`.
shown will increase, even if you are standing still.
* `TIME` - the elapsed time for your run
* `PACE` - the number of minutes it takes you to run a given distance, configured in settings (default 1km) **based on your run so far**
* `HEART` - Your heart rate
* `HEART (BPM)` - Your current heart rate
* `Max BPM` - Your maximum heart rate reached during the run
* `STEPS` - Steps since you started exercising
* `CADENCE` - Steps per second based on your step rate *over the last minute*
* `GPS` - this is green if you have a GPS lock. GPS is turned on automatically
@ -35,7 +36,7 @@ Under `Settings` -> `App` -> `Run` you can change settings for this app.
record GPS/HRM/etc data every time you start a run?
* `Pace` is the distance that pace should be shown over - 1km, 1 mile, 1/2 Marathon or 1 Marathon
* `Boxes` leads to a submenu where you can configure what is shown in each of the 6 boxes on the display.
Available stats are "Time", "Distance", "Steps", "Heart (BPM)", "Pace (avg)", "Pace (curr)", "Speed", and "Cadence".
Available stats are "Time", "Distance", "Steps", "Heart (BPM)", "Max BPM", "Pace (avg)", "Pace (curr)", "Speed", and "Cadence".
Any box set to "-" will display no information.
* Box 1 is the top left (defaults to "Distance")
* Box 2 is the top right (defaults to "Time")

View File

@ -8,6 +8,7 @@
"type": "app",
"tags": "",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"score.app.js","url":"score.app.js"},
{"name":"score.settings.js","url":"score.settings.js"},

View File

@ -7,6 +7,7 @@
"icon": "app.png",
"tags": "tool",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"showimg.app.js","url":"app.js"},
{"name":"showimg.img","url":"app-icon.js","evaluate":true}

View File

@ -0,0 +1 @@
1.0: Initial release on the app repository for Bangle.js 1 and 2

View File

@ -0,0 +1,23 @@
# Stardate Clock
A clock face displaying a stardate along with a "standard" digital/analog clock
in LCARS design.
That design has been made popular by various Star Trek shows. Credits for the
original LCARS designs go to Michael Okuda, copyrights are owned by Paramount Global,
usage of that type of design is permitted freely for non-profit use cases.
The Bangle.js version has been created by Robert Kaiser <kairo@kairo.at>.
The stardate concept used leans on the shows released from the late 80s onward
by using 1000 units per Earth year, but to apply this more cleanly, this split
is applied exactly. Also, to give more relationship to the shows and
incidentally make values look similar to those depicted there, the zero point
is set to the first airing of the original 'Star Trek' series in the US on
Thursday, September 8, 1966, at 8:30 p.m. Eastern Time.
The clock face supports Bangle.js 1 and 2 with some compromises (e.g. the
colors will look best on Bangle.js 1, the font sizes will look best on
Bangle.js 2).
Any tap on the diaply while unlocked switches the "standard" Earth-style clock
between digital and analog display.

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgkiAA0gDRwX/C/4X/C5MO9wBDgnkAIMAAQQXKAItehwECAQIXK8gBEIQIeDC5YAF8EAAIIECC48hE4oYCAogXIkQvHDIgCBiQXHiCPFAIaaCgECJBChDAIsOU4RIJbJwwIDIVEABIYBMJAXOAC8SmYAHmHdABJfBCxAXNCpEyRxoWVgETC46+OkYXHRpxGWC5EwBQMBkQDBiK4DKQMBiAXKNQMggQ2CgI7CkcgC5UjicwkYXCgUxmakBC5kCmERC4MiAoMjgMTC50AC4KYCkcAgYXRPgJFBC6YABgYEBC6iQBC6cRgMgL6ikBR4IXOiR3EX4IXPAgTXDBgIXNgUiiClCAAMikIKBC5YAMC64AXogAGoAX/C6w"))

362
apps/stardateclock/app.js Normal file
View File

@ -0,0 +1,362 @@
// Stardate clock face, by KaiRo.at, 2021-2022
var redrawClock = true;
var clockface = "digital";
// note: Bangle.js 1 has 240x240x16, 2 has 176x176x3 screen
var bpp = g.getBPP ? g.getBPP() : 16;
// Load fonts
Graphics.prototype.setFontAntonio27 = function(scale) {
// Actual height 23 (23 - 1)
g.setFontCustom(atob("AAAAAAGAAAAwAAAGAAAAwAAAGAAAAwAAAAAAAAAAAAAAAAAADAAAA4AAAHAAAAAAAAAAAAAAAAAAAAAA4AAB/AAD/4AH/4AP/wAf/gAD/AAAeAAAAAAAAAAAAA///AP//+D///4eAAPDgAA4cAAHD///4P//+A///gAAAAAAAAAAAAAAYAAAHAAAA4AAAOAAAD///4f///D///4AAAAAAAAAAAAAAAAAAAAAAA/gD4P8B/D/g/4cAfzDgP4Yf/8DD/+AYP/ADAGAAAAAAAAAAAAHwD8B+AfwfwD/DgMA4cDgHDgeA4f///B/3/wH8P8AAAAAAAAAAAAOAAAPwAAP+AAP/wAf8OAf4BwD///4f///D///4AABwAAAGAAAAAAAAAAAAAAD/4Pwf/h/D/4P4cMAHDjgA4cf//Dh//4cH/8AAAAAAAAAAAAAAH//8B///wf///Dg4A4cHAHDg4A4f3//B+f/wHh/8AAAAAAAAAAAAAAcAAADgAA4cAD/DgH/4cH//Dv/4Af/gAD/gAAfAAADgAAAAAAAAAAAAH4f8B///wf///Dg8A4cDAHDg8A4f///B///wH8/8AAAAAAAAAAAAAAH/h4B/+Pwf/5/DgHA4cA4HDgHA4f///B///wH//8AAAAAAAAAAAAAAAAAAAHgeAA8DwAHgeAAAAAAAAAA"), 45, atob("CQcKDAsMDAwMDAwMDAc="), 27+(scale<<8)+(1<<16));
};
Graphics.prototype.setFontAntonio42 = function(scale) {
// Actual height 36 (36 - 1)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAHgAAAAAHgAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAfgAAAAH/gAAAB//gAAAf//gAAH//4AAB//+AAAf//gAAH//4AAAf/+AAAAf/gAAAAf4AAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gAH////+AP/////Af/////gf/////gfAAAAPgeAAAAHgeAAAAHgfAAAAPgf/////gf/////gP/////AH////+AB////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAB4AAAAAB4AAAAADwAAAAAHwAAAAAP/////gf/////gf/////gf/////gf/////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AAPgH/8AD/gP/8AP/gP/8A//gf/8B//gfAAH/ngeAAf+HgeAB/4HgfAH/gHgf//+AHgP//4AHgH//wAHgD/+AAHgAPgAAAAAAAAAAAAAAAAAAAAAAAAAA+AAfwAH+AAf+AP+AAf/AP+AAf/Af+AAf/gfADwAPgeADwAHgeADwAHgfAH4APgf///h/gf/////AP/+///AH/+f/+AB/4H/4AAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAA/gAAAAH/gAAAB//gAAAP//gAAB//HgAAf/wHgAD/8AHgAf/AAHgAf/////gf/////gf/////gf/////gf/////gAAAAHgAAAAAHgAAAAAHAAAAAAAAAAAAAAAAAAAAAAAf//gP8Af//gP+Af//gP/Af//gP/gf/+AAfgeB8AAHgeB4AAHgeB8AAHgeB////geB////geA////AeAf//+AAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///gAD////8AH/////AP/////Af/////gfAPgAfgeAPAAHgeAPAAHgeAPAAHgf+PgAPgf+P///gP+H///AH+H//+AB+B//8AAAAD8AAAAAAAAAAAAAAAAAAAAAAAeAAAAAAeAAAAAAeAAAAPgeAAAP/geAAD//geAA///geAH///geB///+AeP//4AAe//8AAAf//AAAAf/wAAAAf+AAAAAfwAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wH/4AH/8f/+AP/////Af/////gf/////geAH4APgeADgAHgeADgAHgeAHwAHgf/////gf/////gP/////AH/8//+AB/wH/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//gPgAH//4P+AP//8P/Af//+P/AfwB+P/geAAeAPgeAAeAHgeAAeAHgfAAeAPgf/////gP/////AP/////AH////8AA////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4APgAAH4AfgAAH4AfgAAH4AfgAAH4AfgAAD4APgAAAAAAAAAAAAAAA="), 45, atob("DgsPEhESEhISEhISEgo="), 42+(scale<<8)+(1<<16));
};
const fontName = "Antonio27";
const fontNameLarge = "Antonio42";
const fontSize = 1;
const fontSizeLarge = 1;
const fontHeightLarge = 42 * fontSizeLarge;
// LCARS dimensions
if (g.getWidth() < 200) { // Bangle.js 2
const baseUnit1 = 3;
const baseUnit2 = 2;
const baseUnit3 = 7;
}
else {
const baseUnit1 = 5;
const baseUnit2 = 3;
const baseUnit3 = 10;
}
const widgetsHeight = 24;
const sbarWid = baseUnit3 * 5;
const hbarHt = baseUnit1;
const outRad = baseUnit1 * 5;
const inRad = outRad - hbarHt;
const gap = baseUnit2;
const divisionPos = baseUnit3 * 8;
const sbarGapPos = baseUnit3 * 15;
const lowerTop = divisionPos+gap+1;
// Star Trek famously premiered on Thursday, September 8, 1966, at 8:30 p.m.
// See http://www.startrek.com/article/what-if-the-original-star-trek-had-debuted-on-friday-nights
const gSDBase = new Date("September 8, 1966 20:30:00 EST");
const sdatePosBottom = divisionPos - hbarHt - 1;
const sdatePosRight = g.getWidth() - baseUnit2;
const sdateDecimals = 1;
const secondsPerYear = 86400 * 365.2425;
const sdateDecFactor = Math.pow(10, sdateDecimals);
const clockAreaLeft = sbarWid + inRad / 2;
const clockAreaTop = lowerTop + hbarHt + inRad / 2;
const clockWid = g.getWidth() - clockAreaLeft;
const clockHt = g.getHeight() - clockAreaTop;
const ctimePosTop = clockAreaTop + baseUnit1 * 5;
const ctimePosCenter = clockAreaLeft + clockWid / 2;
const cdatePosTop = ctimePosTop + fontHeightLarge;
const cdatePosCenter = clockAreaLeft + clockWid / 2;
const clockCtrX = Math.floor(clockAreaLeft + clockWid / 2);
const clockCtrY = Math.floor(clockAreaTop + clockHt / 2);
const analogRad = Math.floor(Math.min(clockWid, clockHt) / 2);
const analogMainLineLength = baseUnit1 * 2;
const analogSubLineLength = baseUnit1;
const analogHourHandLength = analogRad / 2;
const analogMinuteHandLength = analogRad - analogMainLineLength / 2;
const colorBg = "#000000";
const colorTime = "#9C9CFF";
const colorDate = "#A09090";
const colorStardate = "#FFCF00";
// On low-bpp devices (Bangle.js 2), use basic colors for analog clock.
const colorHours = bpp > 3 ? "#9C9CFF" : "#00FF00";
const colorSeconds = bpp > 3 ? "#E7ADE7" : "#FFFF00";
const colorHands = bpp > 3 ? "#A09090" : "#00FFFF";
const colorLCARSGray = "#A09090";
const colorLCARSOrange = "#FF9F00";
const colorLCARSPink = "#E7ADE7";
const colorLCARSPurple = "#A06060";
const colorLCARSBrown = "#C09070";
// More colors: teal #008484, yellow FFCF00, purple #6050B0
var lastSDateString;
var lastTimeStringToMin;
var lastTimeStringSec;
var lastDateString;
var lastAnalogDate;
function updateStardate() {
var curDate = new Date();
// Note that the millisecond division and the 1000-unit multiplier cancel each other out.
var sdateval = (curDate - gSDBase) / secondsPerYear;
var sdatestring = (Math.floor(sdateval * sdateDecFactor) / sdateDecFactor).toFixed(sdateDecimals);
// Reset the state of the graphics library.
g.reset();
g.setBgColor(colorBg);
// Set Font
g.setFont(fontName, fontSize);
if (lastSDateString) {
// Clear the area where we want to draw the time.
//g.setBgColor("#FF6600"); // for debugging
g.clearRect(sdatePosRight - g.stringWidth(lastSDateString) - 1,
sdatePosBottom - g.getFontHeight(),
sdatePosRight,
sdatePosBottom);
}
// Draw the current stardate.
g.setColor(colorStardate);
g.setFontAlign(1, 1, 0); // Align following string to bottom right.
g.drawString(sdatestring, sdatePosRight, sdatePosBottom);
lastSDateString = sdatestring;
// Schedule next when an update to the last decimal is due.
var mstonextUpdate = (Math.ceil(sdateval * sdateDecFactor) / sdateDecFactor - sdateval) * secondsPerYear;
if (redrawClock) {
setTimeout(updateStardate, mstonextUpdate);
}
}
function updateConventionalTime() {
var curDate = new Date();
if (clockface == "digital") {
drawDigitalClock(curDate);
}
else {
drawAnalogClock(curDate);
}
// Schedule next when an update to the last second is due.
var mstonextUpdate = Math.ceil(curDate / 1000) * 1000 - curDate;
if (redrawClock) {
setTimeout(updateConventionalTime, mstonextUpdate);
}
}
function drawDigitalClock(curDate) {
var timestringToMin = ("0" + curDate.getHours()).substr(-2) + ":"
+ ("0" + curDate.getMinutes()).substr(-2) + ":";
var timestringSec = ("0" + curDate.getSeconds()).substr(-2);
var datestring = "" + curDate.getFullYear() + "-"
+ ("0" + (curDate.getMonth() + 1)).substr(-2) + "-"
+ ("0" + curDate.getDate()).substr(-2);
// Reset the state of the graphics library.
g.reset();
g.setBgColor(colorBg);
// Set Font
g.setFont(fontNameLarge, fontSizeLarge);
var ctimePosLeft = ctimePosCenter - g.stringWidth("12:34:56") / 2;
if (ctimePosLeft + g.stringWidth("00:00:00") > g.getWidth()) {
ctimePosLeft = g.getWidth() - g.stringWidth("00:00:00");
}
g.setColor(colorTime);
if (timestringToMin != lastTimeStringToMin) {
if (lastTimeStringToMin) {
// Clear the area where we want to draw the time.
//g.setBgColor("#FF6600"); // for debugging
g.clearRect(ctimePosLeft,
ctimePosTop,
ctimePosLeft + g.stringWidth(lastTimeStringToMin) + 1,
ctimePosTop + g.getFontHeight());
}
// Draw the current time.
g.drawString(timestringToMin, ctimePosLeft, ctimePosTop);
lastTimeStringToMin = timestringToMin;
}
var ctimePosLeftSec = ctimePosLeft + g.stringWidth(timestringToMin);
if (lastTimeStringSec) {
// Clear the area where we want to draw the seconds.
//g.setBgColor("#FF6600"); // for debugging
g.clearRect(ctimePosLeftSec,
ctimePosTop,
ctimePosLeftSec + g.stringWidth(lastTimeStringSec) + 1,
ctimePosTop + g.getFontHeight());
}
// Draw the current seconds.
g.drawString(timestringSec, ctimePosLeftSec, ctimePosTop);
lastTimeStringSec = timestringSec;
if (datestring != lastDateString) {
// Set Font
g.setFont(fontName, fontSize);
var cdatePosLeft = cdatePosCenter - g.stringWidth("1234-56-78") / 2;
if (lastDateString) {
// Clear the area where we want to draw the time.
//g.setBgColor("#FF6600"); // for debugging
g.clearRect(cdatePosLeft,
cdatePosTop,
cdatePosLeft + g.stringWidth(lastDateString) + 1,
cdatePosTop + g.getFontHeight());
}
// Draw the current date.
g.setColor(colorDate);
//g.setFontAlign(0, -1, 0); // Align following string to bottom right.
g.drawString(datestring, cdatePosLeft, cdatePosTop);
lastDateString = datestring;
}
}
function drawLine(x1, y1, x2, y2, color) {
g.setColor(color);
// On high-bpp devices, use anti-aliasing. Low-bpp (Bangle.js 2) doesn't clear nicely with AA.
if (bpp > 3 && g.drawLineAA) {
g.drawLineAA(x1, y1, x2, y2);
}
else {
g.drawLine(x1, y1, x2, y2);
}
}
function clearLine(x1, y1, x2, y2) {
drawLine(x1, y1, x2, y2, colorBg);
}
function drawAnalogClock(curDate) {
// Reset the state of the graphics library.
g.reset();
g.setBgColor(colorBg);
// Init variables for drawing any seconds we have not drawn.
// If minute changed, we'll set for the full wheel below.
var firstDrawSecond = lastAnalogDate ? lastAnalogDate.getSeconds() + 1 : curDate.getSeconds();
var lastDrawSecond = curDate.getSeconds();
if (!lastAnalogDate || curDate.getMinutes() != lastAnalogDate.getMinutes()) {
// Draw the main hour lines.
//g.setColor("#9C9CFF");
//g.drawCircle(clockCtrX, clockCtrY, analogRad);
for (let i = 0; i < 60; i = i + 15) {
let edgeX = clockCtrX + analogRad * Math.sin(i * Math.PI / 30);
let edgeY = clockCtrY - analogRad * Math.cos(i * Math.PI / 30);
let innerX = clockCtrX + (analogRad - analogMainLineLength) * Math.sin(i * Math.PI / 30);
let innerY = clockCtrY - (analogRad - analogMainLineLength) * Math.cos(i * Math.PI / 30);
drawLine(edgeX, edgeY, innerX, innerY, colorHours);
}
// Set for drawing the full second wheel.
firstDrawSecond = 0;
lastDrawSecond = 59;
}
// Draw the second wheel, or the parts of it that we haven't done yet.
for (let i = firstDrawSecond; i <= lastDrawSecond; i++) {
let edgeX = clockCtrX + analogRad * Math.sin(i * Math.PI / 30);
let edgeY = clockCtrY - analogRad * Math.cos(i * Math.PI / 30);
let innerX = clockCtrX + (analogRad - analogSubLineLength) * Math.sin(i * Math.PI / 30);
let innerY = clockCtrY - (analogRad - analogSubLineLength) * Math.cos(i * Math.PI / 30);
if (i <= curDate.getSeconds()) {
drawLine(edgeX, edgeY, innerX, innerY, colorSeconds);
}
else if (i % 5 == 0) {
drawLine(edgeX, edgeY, innerX, innerY, colorHours);
}
else {
clearLine(edgeX, edgeY, innerX, innerY);
}
}
if (lastAnalogDate) {
// Clear previous hands.
if (curDate.getMinutes() != lastAnalogDate.getMinutes()) {
// Clear hour hand.
let HhAngle = (lastAnalogDate.getHours() + lastAnalogDate.getMinutes() / 60) * Math.PI / 6;
let HhEdgeX = clockCtrX + analogHourHandLength * Math.sin(HhAngle);
let HhEdgeY = clockCtrY - analogHourHandLength * Math.cos(HhAngle);
clearLine(HhEdgeX, HhEdgeY, clockCtrX, clockCtrY);
// Clear minute hand.
let MhEdgeX = clockCtrX + analogMinuteHandLength * Math.sin(lastAnalogDate.getMinutes() * Math.PI / 30);
let MhEdgeY = clockCtrY - analogMinuteHandLength * Math.cos(lastAnalogDate.getMinutes() * Math.PI / 30);
clearLine(MhEdgeX, MhEdgeY, clockCtrX, clockCtrY);
}
}
if (!lastAnalogDate || curDate.getMinutes() != lastAnalogDate.getMinutes()) {
// Draw hour hand.
let HhAngle = (curDate.getHours() + curDate.getMinutes() / 60) * Math.PI / 6;
let HhEdgeX = clockCtrX + analogHourHandLength * Math.sin(HhAngle);
let HhEdgeY = clockCtrY - analogHourHandLength * Math.cos(HhAngle);
drawLine(HhEdgeX, HhEdgeY, clockCtrX, clockCtrY, colorHands);
// Draw minute hand.
let MhEdgeX = clockCtrX + analogMinuteHandLength * Math.sin(curDate.getMinutes() * Math.PI / 30);
let MhEdgeY = clockCtrY - analogMinuteHandLength * Math.cos(curDate.getMinutes() * Math.PI / 30);
drawLine(MhEdgeX, MhEdgeY, clockCtrX, clockCtrY, colorHands);
}
lastAnalogDate = curDate;
}
function switchClockface() {
if (clockface == "digital") {
clockface = "analog";
}
else {
clockface = "digital";
}
// Clear whole lower area.
g.clearRect(clockAreaLeft,clockAreaTop,g.getWidth(),g.getHeight());
lastTimeStringToMin = undefined;
lastTimeStringSec = undefined;
lastDateString = undefined;
lastAnalogDate = undefined;
}
// Clear the screen once, at startup.
g.setBgColor(colorBg);
g.clear();
// Draw LCARS borders.
// Upper section: rounded corner.
g.setColor(colorLCARSGray);
g.fillCircle(outRad, divisionPos - outRad, outRad);
g.fillRect(outRad, divisionPos - outRad, sbarWid + inRad, divisionPos);
g.fillRect(outRad, divisionPos - hbarHt, sbarWid + outRad, divisionPos); // div bar stub
g.fillRect(0, 0, sbarWid, divisionPos - outRad); // side bar
g.setColor(colorBg); // blocked out areas of corner
g.fillCircle(sbarWid + inRad + 1, divisionPos - hbarHt - inRad - 1, inRad);
g.fillRect(sbarWid + 1, divisionPos - outRad * 2, sbarWid + outRad, divisionPos - hbarHt - inRad);
// upper division bar
g.setColor(colorLCARSPurple);
g.fillRect(sbarWid + outRad + gap + 1, divisionPos - hbarHt, g.getWidth(), divisionPos);
// Lower section: rounded corner.
g.setColor(colorLCARSPink);
g.fillCircle(outRad, lowerTop + outRad, outRad);
g.fillRect(outRad, lowerTop, sbarWid + inRad, lowerTop + outRad);
g.fillRect(outRad, lowerTop, sbarWid + outRad, lowerTop + hbarHt); // div bar stub
g.fillRect(0, lowerTop + outRad, sbarWid, sbarGapPos); // side bar
g.setColor(colorBg); // blocked out areas of corner
g.fillCircle(sbarWid + inRad + 1, lowerTop + hbarHt + inRad + 1, inRad);
g.fillRect(sbarWid + 1, lowerTop + hbarHt + inRad, sbarWid + outRad, lowerTop + outRad * 2);
// lower division bar
g.setColor(colorLCARSOrange);
g.fillRect(sbarWid + outRad + gap + 1, lowerTop, g.getWidth(), lowerTop + hbarHt);
// second color of side bar
g.setColor(colorLCARSBrown);
g.fillRect(0, sbarGapPos + gap + 1, sbarWid, g.getHeight());
// Draw immediately at first.
updateStardate();
updateConventionalTime();
// Make sure widgets can be shown.
//g.setColor("#FF0000"); g.fillRect(0, 0, g.getWidth(), widgetsHeight); // debug
Bangle.loadWidgets();
Bangle.drawWidgets();
// Show launcher on button press as usual for a clock face
Bangle.setUI("clock", Bangle.showLauncher);
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower', on => {
if (on) {
redrawClock = true;
// Draw immediately to kick things off.
updateStardate();
updateConventionalTime();
}
else {
redrawClock = false;
}
});
Bangle.on('touch', button => {
// button == 1 is left, 2 is right
switchClockface();
});

BIN
apps/stardateclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

View File

@ -0,0 +1,23 @@
{
"id": "stardateclock",
"name":"Stardate Clock",
"shortName":"Stardate Clock",
"description": "A clock displaying a stardate along with a 'standard' digital/analog clock in LCARS design",
"version":"1.0",
"icon": "app.png",
"type":"clock",
"tags": "clock",
"supports": ["BANGLEJS", "BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{"name": "stardateclock.app.js","url": "app.js"},
{"name": "stardateclock.img","url": "app-icon.js","evaluate": true}
],
"screenshots": [
{"url": "screenshot1.png"},
{"url": "screenshot2.png"},
{"url": "screenshot3.png"},
{"url": "screenshot4.png"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -7,6 +7,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"sunclock.app.js","url":"app.js"},

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS"],
"readme": "README.md",
"storage": [
{"name":"supmariodark.app.js","url":"supmariodark.js"},
{"name":"supmariodark.img","url":"supmariodark-icon.js","evaluate":true},

View File

@ -8,6 +8,7 @@
"type": "bootloader",
"tags": "tool",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"touchmenu.boot.js","url":"touchmenu.boot.js"}
]

View File

@ -8,6 +8,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"waveclk.app.js","url":"app.js"},

View File

@ -15,6 +15,7 @@ print(ExStats.getList());
{name: "Distance", id:"dist"},
{name: "Steps", id:"step"},
{name: "Heart (BPM)", id:"bpm"},
{name: "Max BPM", id:"maxbpm"},
{name: "Pace (avr)", id:"pacea"},
{name: "Pace (current)", id:"pacec"},
{name: "Cadence", id:"caden"},
@ -72,6 +73,7 @@ var state = {
// cadence // steps per minute adjusted if <1 minute
// BPM // beats per minute
// BPMage // how many seconds was BPM set?
// maxBPM // The highest BPM reached while active
// Notifies: 0 for disabled, otherwise how often to notify in meters, seconds, or steps
notify: {
dist: {
@ -159,6 +161,10 @@ Bangle.on("HRM", function(h) {
if (h.confidence>=60) {
state.BPM = h.bpm;
state.BPMage = 0;
if (state.maxBPM < h.bpm) {
state.maxBPM = h.bpm;
if (stats["maxbpm"]) stats["maxbpm"].emit("changed",stats["maxbpm"]);
}
if (stats["bpm"]) stats["bpm"].emit("changed",stats["bpm"]);
}
});
@ -170,6 +176,7 @@ exports.getList = function() {
{name: "Distance", id:"dist"},
{name: "Steps", id:"step"},
{name: "Heart (BPM)", id:"bpm"},
{name: "Max BPM", id:"maxbpm"},
{name: "Pace (avg)", id:"pacea"},
{name: "Pace (curr)", id:"pacec"},
{name: "Speed", id:"speed"},
@ -230,6 +237,14 @@ exports.getStats = function(statIDs, options) {
getString : function() { return state.BPM||"--" },
};
}
if (statIDs.includes("maxbpm")) {
needHRM = true;
stats["maxbpm"]={
title : "Max BPM",
getValue : function() { return state.maxBPM; },
getString : function() { return state.maxBPM||"--" },
};
}
if (statIDs.includes("pacea")) {
needGPS = true;
stats["pacea"]={
@ -299,6 +314,7 @@ exports.getStats = function(statIDs, options) {
state.curSpeed = 0;
state.BPM = 0;
state.BPMage = 0;
state.maxBPM = 0;
state.notify = options.notify;
if (options.notify.dist.increment > 0) {
state.notify.dist.next = state.distance + options.notify.dist.increment;