diff --git a/apps.json b/apps.json index b6192b2e1..19a2e21f2 100644 --- a/apps.json +++ b/apps.json @@ -1,14 +1,15 @@ [ { "id": "boot", "name": "Bootloader", - "tags": "tool,system", + "tags": "tool,system,b2", "type":"bootloader", "icon": "bootloader.png", - "version":"0.22", + "version":"0.27", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "storage": [ {"name":".boot0","url":"boot0.js"}, - {"name":".bootcde","url":"bootloader.js"} + {"name":".bootcde","url":"bootloader.js"}, + {"name":"bootupdate.js","url":"bootupdate.js"} ], "sortorder" : -10 }, @@ -41,21 +42,34 @@ "name": "Launcher (Default)", "shortName":"Launcher", "icon": "app.png", - "version":"0.04", + "version":"0.06", "description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", - "tags": "tool,system,launcher", + "tags": "tool,system,launcher,b2", "type":"launch", "storage": [ {"name":"launch.app.js","url":"app.js"} ], "sortorder" : -10 }, + { "id": "launchb2", + "name": "Launcher (Bangle.js 2)", + "shortName":"Launcher", + "icon": "app.png", + "version":"0.01", + "description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications. It will not work on Bangle.js 1.0.", + "tags": "tool,system,launcher,b2,bno1", + "type":"launch", + "storage": [ + {"name":"launchb2.app.js","url":"app.js"} + ], + "sortorder" : -10 + }, { "id": "about", "name": "About", "icon": "app.png", - "version":"0.07", + "version":"0.08", "description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers", - "tags": "tool,system", + "tags": "tool,system,b2", "allow_emulator":true, "storage": [ {"name":"about.app.js","url":"app.js"}, @@ -67,7 +81,7 @@ "icon": "locale.png", "version":"0.09", "description": "Translations for different countries", - "tags": "tool,system,locale,translate", + "tags": "tool,system,locale,translate,b2", "type": "locale", "custom":"locale.html", "readme": "README.md", @@ -104,7 +118,7 @@ { "id": "welcome", "name": "Welcome", "icon": "app.png", - "version":"0.09", + "version":"0.10", "description": "Appears at first boot and explains how to use Bangle.js", "tags": "start,welcome", "allow_emulator":true, @@ -171,13 +185,12 @@ { "id": "setting", "name": "Settings", "icon": "settings.png", - "version":"0.24", + "version":"0.27", "description": "A menu for setting up Bangle.js", - "tags": "tool,system", + "tags": "tool,system,b2", "readme": "README.md", "storage": [ {"name":"setting.app.js","url":"settings.js"}, - {"name":"setting.boot.js","url":"boot.js"}, {"name":"setting.img","url":"settings-icon.js","evaluate":true} ], "data": [ @@ -191,7 +204,7 @@ "icon": "app.png", "version":"0.11", "description": "Set and respond to alarms", - "tags": "tool,alarm,widget", + "tags": "tool,alarm,widget,b2", "storage": [ {"name":"alarm.app.js","url":"app.js"}, {"name":"alarm.boot.js","url":"boot.js"}, @@ -206,9 +219,9 @@ { "id": "wclock", "name": "Word Clock", "icon": "clock-word.png", - "version":"0.02", + "version":"0.03", "description": "Display Time as Text", - "tags": "clock", + "tags": "clock,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -337,9 +350,9 @@ { "id": "aclock", "name": "Analog Clock", "icon": "clock-analog.png", - "version": "0.14", + "version": "0.15", "description": "An Analog Clock", - "tags": "clock", + "tags": "clock,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -350,9 +363,9 @@ { "id": "clock2x3", "name": "2x3 Pixel Clock", "icon": "clock2x3.png", - "version":"0.04", + "version":"0.05", "description": "This is a simple clock using minimalist 2x3 pixel numerical digits", - "tags": "clock", + "tags": "clock,b2", "type": "clock", "allow_emulator":true, "storage": [ @@ -363,9 +376,9 @@ { "id": "geissclk", "name": "Geiss Clock", "icon": "clock.png", - "version":"0.02", + "version":"0.03", "description": "7 segment clock with animated background in the style of Ryan Geiss' music visualisation. NOTE: The first run will take ~1 minute to do some precalculation", - "tags": "clock", + "tags": "clock,bno2", "type":"clock", "storage": [ {"name":"geissclk.app.js","url":"clock.js"}, @@ -387,13 +400,17 @@ { "id": "trex", "name": "T-Rex", "icon": "trex.png", - "version":"0.02", + "version":"0.03", "description": "T-Rex game in the style of Chrome's offline game", "tags": "game", "allow_emulator":true, "storage": [ {"name":"trex.app.js","url":"trex.js"}, - {"name":"trex.img","url":"trex-icon.js","evaluate":true} + {"name":"trex.img","url":"trex-icon.js","evaluate":true}, + {"name":"trex.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"trex.score", "storageFile": true} ] }, { "id": "astroid", @@ -478,10 +495,11 @@ { "id": "gpsrec", "name": "GPS Recorder", "icon": "app.png", - "version":"0.19", + "version":"0.22", "interface": "interface.html", "description": "Application that allows you to record a GPS track. Can run in background", "tags": "tool,outdoors,gps,widget", + "readme": "README.md", "storage": [ {"name":"gpsrec.app.js","url":"app.js"}, {"name":"gpsrec.img","url":"app-icon.js","evaluate":true}, @@ -551,7 +569,7 @@ { "id": "weather", "name": "Weather", "icon": "icon.png", - "version":"0.03", + "version":"0.05", "description": "Show Gadgetbridge weather report", "readme": "readme.md", "tags": "widget,outdoors", @@ -596,14 +614,25 @@ { "id": "widbat", "name": "Battery Level Widget", "icon": "widget.png", - "version":"0.05", + "version":"0.07", "description": "Show the current battery level and charging status in the top right of the clock", - "tags": "widget,battery", + "tags": "widget,battery,b2", "type":"widget", "storage": [ {"name":"widbat.wid.js","url":"widget.js"} ] }, + { "id": "widlock", + "name": "Lock Widget", + "icon": "widget.png", + "version":"0.03", + "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked", + "tags": "widget,lock,b2", + "type":"widget", + "storage": [ + {"name":"widlock.wid.js","url":"widget.js"} + ] + }, { "id": "widbatpc", "name": "Battery Level Widget (with percentage)", "shortName": "Battery Widget", @@ -641,9 +670,9 @@ { "id": "widbt", "name": "Bluetooth Widget", "icon": "widget.png", - "version":"0.04", + "version":"0.05", "description": "Show the current Bluetooth connection status in the top right of the clock", - "tags": "widget,bluetooth", + "tags": "widget,bluetooth,b2", "type":"widget", "storage": [ {"name":"widbt.wid.js","url":"widget.js"} @@ -679,7 +708,7 @@ { "id": "hrm", "name": "Heart Rate Monitor", "icon": "heartrate.png", - "version":"0.03", + "version":"0.04", "description": "Measure your heart rate and see live sensor data", "tags": "health", "storage": [ @@ -861,9 +890,9 @@ { "id": "sclock", "name": "Simple Clock", "icon": "clock-simple.png", - "version":"0.04", + "version":"0.05", "description": "A Simple Digital Clock", - "tags": "clock", + "tags": "clock,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -871,10 +900,23 @@ {"name":"sclock.img","url":"clock-simple-icon.js","evaluate":true} ] }, + { "id": "s7clk", + "name": "Simple 7 segment Clock", + "icon": "icon.png", + "version":"0.03", + "description": "A simple 7 segment Clock with date", + "tags": "clock,b2", + "type":"clock", + "allow_emulator":true, + "storage": [ + {"name":"s7clk.app.js","url":"app.js"}, + {"name":"s7clk.img","url":"icon.js","evaluate":true} + ] + }, { "id": "vibrclock", "name": "Vibrate Clock", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "When BTN1 is pressed, vibrate out the time as a series of buzzes, one digit at a time. Hours, then Minutes. Zero is signified by one long buzz. Otherwise a simple digital clock.", "tags": "clock", "type":"clock", @@ -900,9 +942,9 @@ { "id": "dclock", "name": "Dev Clock", "icon": "clock-dev.png", - "version":"0.09", + "version":"0.10", "description": "A Digital Clock including timestamp (tst), beats(@), days in current month (dm) and days since new moon (l)", - "tags": "clock", + "tags": "clock,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -1026,7 +1068,7 @@ { "id": "bclock", "name": "Binary Clock", "icon": "clock-binary.png", - "version":"0.02", + "version":"0.03", "description": "A simple binary clock watch face", "tags": "clock", "type":"clock", @@ -1102,9 +1144,9 @@ "name": "Large Digit Blob Clock", "shortName" : "Blob Clock", "icon": "clock-blob.png", - "version":"0.05", + "version":"0.06", "description": "A clock with big digits", - "tags": "clock", + "tags": "clock,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -1115,9 +1157,9 @@ { "id": "boldclk", "name": "Bold Clock", "icon": "bold_clock.png", - "version":"0.03", + "version":"0.04", "description": "Simple, readable and practical clock", - "tags": "clock", + "tags": "clock,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -1128,7 +1170,7 @@ { "id": "widclk", "name": "Digital clock widget", "icon": "widget.png", - "version":"0.04", + "version":"0.05", "description": "A simple digital clock widget", "tags": "widget,clock", "type":"widget", @@ -1139,9 +1181,9 @@ { "id": "widpedom", "name": "Pedometer widget", "icon": "widget.png", - "version":"0.12", + "version":"0.14", "description": "Daily pedometer widget", - "tags": "widget", + "tags": "widget,b2", "type":"widget", "storage": [ {"name":"widpedom.wid.js","url":"widget.js"}, @@ -1151,7 +1193,7 @@ { "id": "berlinc", "name": "Berlin Clock", "icon": "berlin-clock.png", - "version":"0.03", + "version":"0.04", "description": "Berlin Clock (see https://en.wikipedia.org/wiki/Mengenlehreuhr)", "tags": "clock", "type":"clock", @@ -1164,9 +1206,9 @@ { "id": "ctrclk", "name": "Centerclock", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "Watch-centered digital 24h clock with date in dd.mm.yyyy format.", - "tags": "clock", + "tags": "clock,bno2", "type":"clock", "allow_emulator":true, "storage": [ @@ -1204,9 +1246,9 @@ "id": "pipboy", "name": "Pipboy", "icon": "app.png", - "version": "0.03", + "version": "0.04", "description": "Pipboy themed clock", - "tags": "clock", + "tags": "clock,bno2", "type":"clock", "allow_emulator":true, "storage": [ @@ -1285,9 +1327,9 @@ "name": "Commandline-Clock", "shortName":"CLI-Clock", "icon": "app.png", - "version":"0.11", + "version":"0.13", "description": "Simple CLI-Styled Clock", - "tags": "clock,cli,command,bash,shell", + "tags": "clock,cli,command,bash,shell,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -1309,10 +1351,11 @@ { "id": "barclock", "name": "Bar Clock", "icon": "clock-bar.png", - "version":"0.05", + "version":"0.07", "description": "A simple digital clock showing seconds as a bar", "tags": "clock", "type":"clock", + "readme": "README.md", "allow_emulator":true, "storage": [ {"name":"barclock.app.js","url":"clock-bar.js"}, @@ -1322,9 +1365,9 @@ { "id": "dotclock", "name": "Dot Clock", "icon": "clock-dot.png", - "version":"0.02", + "version":"0.03", "description": "A Minimal Dot Analog Clock", - "tags": "clock", + "tags": "clock,b2", "type":"clock", "allow_emulator":true, "storage": [ @@ -1459,9 +1502,9 @@ "name": "OpenStreetMap", "shortName":"OpenStMap", "icon": "app.png", - "version":"0.05", + "version":"0.08", "description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are", - "tags": "outdoors,gps", + "tags": "outdoors,gps,b2", "custom": "custom.html", "storage": [ {"name":"openstmap","url":"openstmap.js"}, @@ -1526,9 +1569,9 @@ "name": "Dev Stopwatch", "shortName":"Dev Stopwatch", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "Stopwatch with 5 laps supported (cyclically replaced)", - "tags": "stopwatch, chrono, timer, chronometer", + "tags": "stopwatch,chrono,timer,chronometer,b2", "allow_emulator":true, "storage": [ {"name":"devstopwatch.app.js","url":"app.js"}, @@ -1567,7 +1610,7 @@ "name": "Numerals Clock", "shortName": "Numerals Clock", "icon": "numerals.png", - "version":"0.08", + "version":"0.09", "description": "A simple big numerals clock", "tags": "numerals,clock", "type":"clock", @@ -1853,9 +1896,9 @@ { "id": "calendar", "name": "Calendar", "icon": "calendar.png", - "version": "0.01", + "version": "0.02", "description": "Simple calendar", - "tags": "calendar", + "tags": "calendar,b2", "readme": "README.md", "allow_emulator": true, "storage": [ @@ -1981,7 +2024,7 @@ "id": "beebclock", "name": "Beeb Clock", "icon": "beebclock.png", - "version":"0.03", + "version":"0.04", "description": "Clock face that may be coincidentally familiar to BBC viewers", "tags": "clock", "type": "clock", @@ -2025,9 +2068,9 @@ "name": "Time Traveller's Chronometer", "shortName": "Time Travel Clock", "icon": "gallifr.png", - "version": "0.01", + "version": "0.02", "description": "A clock for time travellers. The light pie segment shows the minutes, the black circle, the hour. The dial itself reads 'time' just in case you forget.", - "tags": "clock", + "tags": "clock,b2", "readme": "README.md", "type": "clock", "allow_emulator":true, @@ -2109,7 +2152,7 @@ "name": "Binary Clock", "shortName":"Binary Clock", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "A binary clock with hours and minutes. BTN1 toggles a digital clock.", "tags": "clock,binary", "type": "clock", @@ -2136,9 +2179,9 @@ "name": "Animated Clock", "shortName":"Anim Clock", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "An animated clock face using Mark Ferrari's amazing 8 bit game art and palette cycling: http://www.markferrari.com/art/8bit-game-art", - "tags": "clock,animated", + "tags": "clock,animated,bno2", "type": "clock", "storage": [ {"name":"animclk.app.js","url":"app.js"}, @@ -2152,9 +2195,9 @@ "name": "Analog Clock (Image background)", "shortName":"Analog Clock", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "An analog clock with an image background", - "tags": "clock", + "tags": "clock,bno2", "type": "clock", "storage": [ {"name":"analogimgclk.app.js","url":"app.js"}, @@ -2167,7 +2210,7 @@ "name": "Vertical watch face", "shortName":"Vertical Face", "icon": "app.png", - "version":"0.08", + "version":"0.09", "description": "A simple vertical watch face with the date. Heart rate monitor is toggled with BTN1", "tags": "clock", "type":"clock", @@ -2285,7 +2328,7 @@ { "id": "multiclock", "name": "Multi Clock", "icon": "multiclock.png", - "version":"0.12", + "version":"0.13", "description": "Clock with multiple faces - Big, Analogue, Digital, Text, Time-Date.\n Switch between faces with BTN1 & BTN3", "readme": "README.md", "tags": "clock", @@ -2472,7 +2515,7 @@ "name": "World Clock - 4 time zones", "shortName":"World Clock", "icon": "app.png", - "version":"0.03", + "version":"0.04", "description": "Current time zone plus up to four others", "tags": "clock", "type" : "clock", @@ -2490,9 +2533,9 @@ "name": "Digital Clock Face", "shortName":"Digi Clock", "icon": "digiclock.png", - "version":"0.01", + "version":"0.02", "description": "A simple digital clock with the time, day, month, and year", - "tags": "clock", + "tags": "clock,bno2", "type" : "clock", "storage": [ {"name":"digiclock.app.js","url":"digiclock.js"}, @@ -2750,7 +2793,7 @@ { "id": "astral", "name": "Astral Clock", "icon": "app-icon.png", - "version":"0.02", + "version":"0.03", "readme": "README.md", "description": "Clock that calculates and displays Alt Az positions of all planets, Sun as well as several other astronomy targets (customizable) and current Moon phase. Coordinates are calculated by GPS & time and onscreen compass assists orienting. See Readme before using.", "tags": "clock", @@ -2809,7 +2852,7 @@ "name": "De-Stress", "shortName":"De-Stress", "icon": "app.png", - "version":"0.01", + "version":"0.02", "description": "Simple haptic heartbeat", "storage": [ {"name":"de-stress.app.js","url":"app.js"}, @@ -3046,7 +3089,7 @@ "version":"0.01", "description": "Displays RGB565 and RGB888 colors, its name and code in screen.", "readme": "README.md", - "tags": "Color, input,buttons,touch,UI", + "tags": "Color,input,buttons,touch,UI,bno2", "storage": [ {"name":"color_catalog.app.js","url":"app.js"}, {"name":"color_catalog.img","url":"app-icon.js","evaluate":true} @@ -3136,7 +3179,7 @@ { "id": "kitchen", "name": "Kitchen Combo", "icon": "kitchen.png", - "version":"0.10", + "version":"0.12", "description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later", "tags": "tool,outdoors,gps", "type":"clock", @@ -3144,11 +3187,9 @@ "interface":"waypoints.html", "storage": [ {"name":"kitchen.app.js","url":"kitchen.app.js"}, - {"name":"stepo.kit.js","url":"stepo.kit.js"}, + {"name":"stepo2.kit.js","url":"stepo2.kit.js"}, + {"name":"swatch.kit.js","url":"swatch.kit.js"}, {"name":"gps.kit.js","url":"gps.kit.js"}, - {"name":"digi.kit.js","url":"digi.kit.js"}, - {"name":"heart.kit.js","url":"heart.kit.js"}, - {"name":"swatch.kit.js","url":"swatch.kit.js"}, {"name":"compass.kit.js","url":"compass.kit.js"}, {"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true} ], @@ -3210,7 +3251,6 @@ "readme": "README.md", "description": "An Omnitrix Showpiece", "tags": "game", - "readme": "README.md", "storage": [ {"name":"omnitrix.app.js","url":"omnitrix.app.js"}, {"name":"omnitrix.img","url":"omnitrix.icon.js","evaluate":true} @@ -3220,7 +3260,7 @@ "name": "Bat Clock", "shortName":"Bat Clock", "icon": "bat-clock.png", - "version":"0.01", + "version":"0.02", "description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.", "tags": "clock", "type": "clock", @@ -3244,5 +3284,62 @@ {"name":"doztime.app.js","url":"app.js"}, {"name":"doztime.img","url":"app-icon.js","evaluate":true} ] +}, +{ "id":"gbtwist", + "name":"Gadgetbridge Twist Control", + "shortName":"Twist Control", + "icon":"app.png", + "version":"0.01", + "description":"Shake your wrist to control your music app via Gadgetbridge", + "tags":"tools,bluetooth,gadgetbridge,music", + "type":"app", + "allow_emulator":false, + "readme": "README.md", + "storage": [ + {"name":"gbtwist.app.js","url":"app.js"}, + {"name":"gbtwist.img","url":"app-icon.js","evaluate":true} + ] +}, +{ "id": "thermom", + "name": "Thermometer", + "icon": "app.png", + "version":"0.02", + "description": "Displays the current temperature, updated every 20 seconds", + "tags": "tool", + "allow_emulator":true, + "storage": [ + {"name":"thermom.app.js","url":"app.js"}, + {"name":"thermom.img","url":"app-icon.js","evaluate":true} + ] +}, +{ "id": "mysticdock", + "name": "Mystic Dock", + "icon": "mystic-dock.png", + "version":"1.00", + "description": "A retro-inspired dockface that displays the current time and battery charge while plugged in, and which features an interactive mode that shows the time, date, and a rotating data display line.", + "tags": "dock", + "type":"dock", + "readme": "README.md", + "storage": [ + {"name":"mysticdock.app.js","url":"mystic-dock-app.js"}, + {"name":"mysticdock.boot.js","url":"mystic-dock-boot.js"}, + {"name":"mysticdock.settings.js","url":"mystic-dock-settings.js"}, + {"name":"mysticdock.img","url":"mystic-dock-icon.js","evaluate":true} + ] +}, +{ "id": "mysticclock", + "name": "Mystic Clock", + "icon": "mystic-clock.png", + "version":"1.00", + "description": "A retro-inspired watchface featuring time, date, and an interactive data display line.", + "tags": "clock", + "type":"clock", + "readme": "README.md", + "allow_emulator":true, + "storage": [ + {"name":"mysticclock.app.js","url":"mystic-clock-app.js"}, + {"name":"mysticclock.settings.js","url":"mystic-clock-settings.js"}, + {"name":"mysticclock.img","url":"mystic-clock-icon.js","evaluate":true} + ] } ] diff --git a/apps/about/ChangeLog b/apps/about/ChangeLog index 22409a4ec..62e8d0126 100644 --- a/apps/about/ChangeLog +++ b/apps/about/ChangeLog @@ -5,3 +5,4 @@ 0.05: Actual pixels as of 27 Apr 2020 0.06: Actual pixels as of 12 Jun 2020 0.07: Pressing a button now exits immediately (fix #618) +0.08: Make about (mostly) work on non-240px screens diff --git a/apps/about/app.js b/apps/about/app.js index a9c6854d9..b95b8bbd0 100644 --- a/apps/about/app.js +++ b/apps/about/app.js @@ -5,8 +5,12 @@ var s = require("Storage"); g.clear(1); g.setFont("6x8"); var y = 24, h=8; +if (g.getWidth()>=240) { g.drawImage(require("heatshrink").decompress(atob("vE4gQZWg//AAI3Zh4dCoAd6wAd64Ad2j4d6l4dcn4dC6Adc+AdYv4dUggHG//kgN//AGB1WkDpkOAwsH/gDBgJ4CTRwdGl6RDl/0gHQgJeMDo2/AgcDIAIkBnAdRgJyCAAQdDlgdRgZPDgbWBDoUcDqMPRYcJgEfoA7Uh9AAgQ1BEgIdBngdRKQIACmBbB6AdB2gdRnoEDyB+C8tbbQVpgNAqOkAwMGyEQDoMB1AIBvgdDPYMC+H//7zBg//+fAA4OAgH//twDoMv/4WB3iyEAAPwHINvTYMAv/A/sC6BmBh/wDoP4gIuBdwayBAAP/DoMH4F4ToQSB+EPJQUOgKmDBgIABhAdFB4L7BgfAAYNwjpKChwJBTIQdDiAdFgHgAYIdDmDaCO4MD9Wq14dM+CdCDoU0nDjChyhBAAIdFsgdTZgaVDmPYLJk0LIodDaIcxcILRDSo80jiVECgUAvgDCmG0YQTRHDoTRBgLRCMwJDBnodDeAMDKoUvAIU/DocD6ELDoKRCAIM/LIcGG4PQUIKCBU4PzDoaEB/p3BFQKKCh9ADoXsKIVVqonCtVBoFQcAUKyFwghdB3IPBCwJZCAQMfEgQAL2AGFgZJBDoZgDABEMWYQJFgLwCkACB/gdLWYMCfoQAE35BEDpkH8EfdgYADl4mDl68BABazBFBA2CgK8CABcBUZP/8kBv58CAC1//4ABUQwASn4dgOxoALl4dC4AdYj4d6h4d+wAd6oAd2g4dCAwQA=")),120,y); g.drawString("BANGLEJS.COM",120,y-4); +} else { + y=-(4+h); // small screen, start right at top +} g.drawString("Powered by Espruino",0,y+=4+h); g.drawString("Version "+ENV.VERSION,0,y+=h); g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h); @@ -25,13 +29,15 @@ g.drawString("Storage: "+(require("Storage").getFree()>>10)+"k free",0,y+=h); if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+"k total",0,y+=h); if (ENV.SPIFLASH) g.drawString("SPI Flash: "+(ENV.SPIFLASH>>10)+"k",0,y+=h); g.setFontAlign(0,-1); -g.drawString(NRF.getAddress(),120,232); g.flip(); // Pixel chooser image -g.drawImage(require("heatshrink").decompress(atob("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")),0,135); +g.drawImage(require("heatshrink").decompress(atob("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")),0,y+19); +g.drawString(NRF.getAddress(),g.getWidth()/2,g.getHeight()-8,true); g.flip(); setWatch(_=>load(), BTN1); -setWatch(_=>load(), BTN2); -setWatch(_=>load(), BTN3); +if (global.BTN2) { + setWatch(_=>load(), BTN2); + setWatch(_=>load(), BTN3); +} diff --git a/apps/aclock/ChangeLog b/apps/aclock/ChangeLog index 77d4ea646..aa910b6f6 100644 --- a/apps/aclock/ChangeLog +++ b/apps/aclock/ChangeLog @@ -9,3 +9,4 @@ 0.12: Fix regression after 0.11 0.13: Fix broken date padding (fix #376) 0.14: Remove hardcoded hour buzz (you can install widchime if you miss it) +0.15: Add color scheme support diff --git a/apps/aclock/clock-analog.js b/apps/aclock/clock-analog.js index 7516a0eb7..146f022fc 100644 --- a/apps/aclock/clock-analog.js +++ b/apps/aclock/clock-analog.js @@ -2,13 +2,16 @@ const locale = require('locale'); const p = Math.PI / 2; const pRad = Math.PI / 180; -const faceWidth = 100; // watch face radius (240/2 - 24px for widget area) +const faceWidth = g.getWidth()/2 - 20; // watch face radius (240/2 - 24px for widget area) const widgetHeight=24+1; let timer = null; let currentDate = new Date(); const centerX = g.getWidth() / 2; const centerY = (g.getWidth() / 2) + widgetHeight/2; - +g.theme.dark=false; +let colSecA = g.theme.dark ? "#00A" : "#58F"; // before the second +let colSecB = g.theme.dark ? "#58F" : "#00A"; // after the second +let colSec1 = g.theme.dark ? "#F83" : "#000"; // ON the second const seconds = (angle) => { const a = angle * pRad; @@ -46,40 +49,35 @@ const drawAll = () => { // draw all secs for (let i = 0; i < 60; i++) { - if (i > currentSec) { - g.setColor(0, 0, 0.6); - } else { - g.setColor(0.3, 0.3, 1); - } + g.setColor((i > currentSec) ? colSecA : colSecB); seconds((360 * i) / 60); } onSecond(); - }; const resetSeconds = () => { - g.setColor(0, 0, 0.6); + g.setColor(colSecA); for (let i = 0; i < 60; i++) { seconds((360 * i) / 60); } }; const onSecond = () => { - g.setColor(0.3, 0.3, 1); + g.setColor(colSecB); seconds((360 * currentDate.getSeconds()) / 60); if (currentDate.getSeconds() === 59) { resetSeconds(); onMinute(); } - g.setColor(1, 0.7, 0.2); + g.setColor(colSec1); currentDate = new Date(); seconds((360 * currentDate.getSeconds()) / 60); - g.setColor(1, 1, 1); + g.setColor(g.theme.fg); }; const drawDate = () => { g.reset(); - g.setColor(1, 0, 0); + g.setColor("#f00"); g.setFont('6x8', 2); const dayString = locale.dow(currentDate, true); @@ -89,7 +87,7 @@ const drawDate = () => { // console.log(`${dayString}|${dateString}`); // center date const l = (g.getWidth() - g.stringWidth(dateDisplay)) / 2; - const t = centerY + 37; + const t = centerY + faceWidth*0.37; g.drawString(dateDisplay, l, t, true); // console.log(l, t); }; @@ -99,7 +97,7 @@ const onMinute = () => { resetSeconds(); } // clear existing hands - g.setColor(0, 0, 0); + g.setColor(g.theme.bg); // Hour hand((360 * (currentDate.getHours() + currentDate.getMinutes() / 60)) / 12, -8, faceWidth - 35); // Minute @@ -107,10 +105,10 @@ const onMinute = () => { // get new date, then draw new hands currentDate = new Date(); - g.setColor(1, 0.9, 0.9); + g.setColor(g.theme.fg); // Hour hand((360 * (currentDate.getHours() + currentDate.getMinutes() / 60)) / 12, -8, faceWidth - 35); - g.setColor(1, 1, 0.9); + g.setColor(g.theme.fg); // Minute hand((360 * currentDate.getMinutes()) / 60, -8, faceWidth - 10); drawDate(); @@ -137,8 +135,9 @@ g.clear(); resetSeconds(); startTimers(); drawAll(); + +// Show launcher when button pressed +Bangle.setUI("clock"); + Bangle.loadWidgets(); Bangle.drawWidgets(); - -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); diff --git a/apps/analogimgclk/ChangeLog b/apps/analogimgclk/ChangeLog index 864afc91e..877ecc04d 100644 --- a/apps/analogimgclk/ChangeLog +++ b/apps/analogimgclk/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Add BTN2 -> launcher +0.03: Update to use setUI diff --git a/apps/analogimgclk/app.js b/apps/analogimgclk/app.js index 99dace78e..1aea97961 100644 --- a/apps/analogimgclk/app.js +++ b/apps/analogimgclk/app.js @@ -114,5 +114,5 @@ if (g.drawImages) { E.showMessage("Please update\nBangle.js firmware\nto use this clock","analogimgclk"); } -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/animclk/ChangeLog b/apps/animclk/ChangeLog index 7852105a0..348448c34 100644 --- a/apps/animclk/ChangeLog +++ b/apps/animclk/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Fix bug if image clock wasn't installed +0.03: Update to use setUI diff --git a/apps/animclk/app.js b/apps/animclk/app.js index ced5372a0..4bf63daf6 100644 --- a/apps/animclk/app.js +++ b/apps/animclk/app.js @@ -102,5 +102,5 @@ if (g.drawImages) { } else { E.showMessage("Please update\nBangle.js firmware\nto use this clock","animclk"); } -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/arrow/README.md b/apps/arrow/README.md index 3b439711c..4b77dbc42 100644 --- a/apps/arrow/README.md +++ b/apps/arrow/README.md @@ -36,6 +36,10 @@ charge. *BTN3* - invokes calibration ( can be cancelled if pressed accidentally) +## Issues +* detect when calibration data is missing + ## Acknowledgement This app is based in the work done by [jeffmer](https://github.com/jeffmer/JeffsBangleAppsDev) + diff --git a/apps/astral/ChangeLog b/apps/astral/ChangeLog index 7a9d48bf1..a51c96760 100644 --- a/apps/astral/ChangeLog +++ b/apps/astral/ChangeLog @@ -1,2 +1,3 @@ 0.01: Create astral clock app 0.02: Fixed Whirlpool galaxy RA/DA, larger compass display, fixed moonphase overlapping battery widget +0.03: Update to use Bangle.setUI instead of setWatch diff --git a/apps/astral/app.js b/apps/astral/app.js index 5d2d16e8f..c445463f2 100644 --- a/apps/astral/app.js +++ b/apps/astral/app.js @@ -503,8 +503,8 @@ function coord_to_horizon(utc, ra, dec, lat, lon, h) { } // -// "mean_sidereal_time" returns the Mean Sidereal Time in units of degrees. -// Use lon = 0 to get the Greenwich MST. +// "mean_sidereal_time" returns the Mean Sidereal Time in units of degrees. +// Use lon = 0 to get the Greenwich MST. // East longitudes are positive; West longitudes are negative // // returns: time in degrees @@ -523,7 +523,7 @@ function mean_sidereal_time(lon) { var c = Math.floor(365.25 * year); var da = Math.floor(30.6001 * (month + 1)); - // days since J2000.0 + // days since J2000.0 var jd = b + c + da - 730550.5 + day + (hour + mins / 60.0 + secs / 3600.0) / 24.0; @@ -796,10 +796,11 @@ Bangle.on('lcdPower', on => { Bangle.setCompassPower(1); Bangle.setGPSPower(1); -// Buttons -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); +// Show launcher when button pressed +Bangle.setClockMode(); -setWatch(function () { +Bangle.setUI("clockupdown", btn => { + if (btn==0) { if (!processing) { if (!modeswitch) { modeswitch = true; @@ -809,12 +810,11 @@ setWatch(function () { else modeswitch = false; } -}, BTN3, { repeat: true }); - -setWatch(function () { + } else { if (!processing) ready_to_compute = true; -}, BTN1, { repeat: true }); + } +}); setWatch(function () { if (!astral_settings.astral_default) { diff --git a/apps/banglerun/ChangeLog b/apps/banglerun/ChangeLog old mode 100755 new mode 100644 diff --git a/apps/barclock/ChangeLog b/apps/barclock/ChangeLog index 616ee66e9..9589a1902 100644 --- a/apps/barclock/ChangeLog +++ b/apps/barclock/ChangeLog @@ -2,4 +2,6 @@ 0.02: Apply locale, 12-hour setting 0.03: Fix dates drawing over each other at midnight 0.04: Small bugfix -0.05: Clock does not start if app Languages is not installed \ No newline at end of file +0.05: Clock does not start if app Languages is not installed +0.06: Improve accuracy +0.07: Update to use Bangle.setUI instead of setWatch diff --git a/apps/barclock/README.md b/apps/barclock/README.md new file mode 100644 index 000000000..4b92313c5 --- /dev/null +++ b/apps/barclock/README.md @@ -0,0 +1,6 @@ +# Bar Clock +A simple digital clock showing seconds as a horizontal bar. + +| 24hr style | 12hr style | +| --- | --- | +| ![24-hour bar clock](screenshot.png) | ![12-hour bar clock with meridian](screenshot_pm.png) | diff --git a/apps/barclock/clock-bar.js b/apps/barclock/clock-bar.js index 0f2609298..5069faa39 100644 --- a/apps/barclock/clock-bar.js +++ b/apps/barclock/clock-bar.js @@ -2,170 +2,168 @@ /** * A simple digital clock showing seconds as a bar **/ -{ - // Check settings for what type our clock should be - const is12Hour = (require('Storage').readJSON('setting.json', 1) || {})['12hour'] - let locale = require('locale') - { // add some more info to locale - let date = new Date() - date.setFullYear(1111) - date.setMonth(1, 3) // februari: months are zero-indexed - const localized = locale.date(date, true) - locale.dayFirst = /3.*2/.test(localized) - - locale.hasMeridian = false - if(typeof locale.meridian === 'function') { // function does not exists if languages app is not installed - locale.hasMeridian = (locale.meridian(date) !== '') - } - - } - const screen = { - width: g.getWidth(), - height: g.getWidth(), - middle: g.getWidth() / 2, - center: g.getHeight() / 2, +// Check settings for what type our clock should be +const is12Hour = (require('Storage').readJSON('setting.json', 1) || {})['12hour'] +let locale = require('locale') +{ // add some more info to locale + let date = new Date() + date.setFullYear(1111) + date.setMonth(1, 3) // februari: months are zero-indexed + const localized = locale.date(date, true) + locale.dayFirst = /3.*2/.test(localized) + + locale.hasMeridian = false + if(typeof locale.meridian === 'function') { // function does not exists if languages app is not installed + locale.hasMeridian = (locale.meridian(date) !== '') } - // hardcoded "settings" - const settings = { - time: { +} +const screen = { + width: g.getWidth(), + height: g.getWidth(), + middle: g.getWidth() / 2, + center: g.getHeight() / 2, +} + +// hardcoded "settings" +const settings = { + time: { + color: -1, + font: '6x8', + size: (is12Hour && locale.hasMeridian) ? 6 : 8, + middle: screen.middle, + center: screen.center, + ampm: { color: -1, font: '6x8', - size: (is12Hour && locale.hasMeridian) ? 6 : 8, - middle: screen.middle, - center: screen.center, - ampm: { - color: -1, - font: '6x8', - size: 2, - }, + size: 2, }, - date: { - color: -1, - font: 'Vector', - size: 20, - middle: screen.height - 20, // at bottom of screen - center: screen.center, - }, - bar: { - color: -1, - top: 155, // just below time - thickness: 6, // matches 24h time "pixel" size - }, - } - - const SECONDS_PER_MINUTE = 60 - - const timeText = function (date) { - if (!is12Hour) { - return locale.time(date, true) - } - const date12 = new Date(date.getTime()) - const hours = date12.getHours() - if (hours === 0) { - date12.setHours(12) - } else if (hours > 12) { - date12.setHours(hours - 12) - } - return locale.time(date12, true) - } - const ampmText = function (date) { - return is12Hour ? locale.meridian(date) : '' - } - - const dateText = function (date) { - const dayName = locale.dow(date, true), - month = locale.month(date, true), - day = date.getDate() - const dayMonth = locale.dayFirst ? `${day} ${month}` : `${month} ${day}` - return `${dayName} ${dayMonth}` - } - - const drawDateTime = function (date) { - const t = settings.time - g.setColor(t.color) - g.setFont(t.font, t.size) - g.setFontAlign(0, 0) // centered - g.drawString(timeText(date), t.center, t.middle, true) - if (is12Hour && locale.hasMeridian) { - const a = settings.time.ampm - g.setColor(a.color) - g.setFont(a.font, a.size) - g.setFontAlign(1, -1) // right top - // at right edge of screen, aligned with time bottom - const left = screen.width - a.size * 2, - top = t.middle + t.size - a.size - g.drawString(ampmText(date), left, top, true) - } - - const d = settings.date - g.setColor(d.color) - g.setFont(d.font, d.size) - g.setFontAlign(0, 0) // centered - g.drawString(dateText(date), d.center, d.middle, true) - } - - const drawBar = function (date) { - const b = settings.bar - const seconds = date.getSeconds() - if (seconds === 0) { - // zero-size rect stills draws one line of pixels, we don't want that - return - } - const fraction = seconds / SECONDS_PER_MINUTE, - width = fraction * screen.width - g.setColor(b.color) - g.fillRect(0, b.top, width, b.top + b.thickness) - } - - const clearScreen = function () { - g.setColor(0) - const timeTop = settings.time.middle - (settings.time.size * 4) - g.fillRect(0, timeTop, screen.width, screen.height) - } - - let lastSeconds - const tick = function () { - g.reset() - const date = new Date() - const seconds = date.getSeconds() - if (lastSeconds > seconds) { - // new minute - clearScreen() - drawDateTime(date) - } - // the bar only gets larger, so drawing on top of the previous one is fine - drawBar(date) - - lastSeconds = seconds - } - - let iTick - const start = function () { - lastSeconds = 99 // force redraw - tick() - iTick = setInterval(tick, 1000) - } - const stop = function () { - if (iTick) { - clearInterval(iTick) - iTick = undefined - } - } - - // clean app screen - g.clear() - Bangle.loadWidgets() - Bangle.drawWidgets() - // Show launcher when middle button pressed - setWatch(Bangle.showLauncher, BTN2, {repeat: false, edge: 'falling'}) - - Bangle.on('lcdPower', function (on) { - if (on) { - start() - } else { - stop() - } - }) - start() + }, + date: { + color: -1, + font: 'Vector', + size: 20, + middle: screen.height - 20, // at bottom of screen + center: screen.center, + }, + bar: { + color: -1, + top: 155, // just below time + thickness: 6, // matches 24h time "pixel" size + }, } + +const SECONDS_PER_MINUTE = 60 + +const timeText = function (date) { + if (!is12Hour) { + return locale.time(date, true) + } + const date12 = new Date(date.getTime()) + const hours = date12.getHours() + if (hours === 0) { + date12.setHours(12) + } else if (hours > 12) { + date12.setHours(hours - 12) + } + return locale.time(date12, true) +} +const ampmText = function (date) { + return is12Hour ? locale.meridian(date) : '' +} + +const dateText = function (date) { + const dayName = locale.dow(date, true), + month = locale.month(date, true), + day = date.getDate() + const dayMonth = locale.dayFirst ? `${day} ${month}` : `${month} ${day}` + return `${dayName} ${dayMonth}` +} + +const drawDateTime = function (date) { + const t = settings.time + g.setColor(t.color) + g.setFont(t.font, t.size) + g.setFontAlign(0, 0) // centered + g.drawString(timeText(date), t.center, t.middle, true) + if (is12Hour && locale.hasMeridian) { + const a = settings.time.ampm + g.setColor(a.color) + g.setFont(a.font, a.size) + g.setFontAlign(1, -1) // right top + // at right edge of screen, aligned with time bottom + const left = screen.width - a.size * 2, + top = t.middle + t.size - a.size + g.drawString(ampmText(date), left, top, true) + } + + const d = settings.date + g.setColor(d.color) + g.setFont(d.font, d.size) + g.setFontAlign(0, 0) // centered + g.drawString(dateText(date), d.center, d.middle, true) +} + +const drawBar = function (date) { + const b = settings.bar + const seconds = date.getSeconds() + if (seconds === 0) { + // zero-size rect stills draws one line of pixels, we don't want that + return + } + const fraction = seconds / SECONDS_PER_MINUTE, + width = fraction * screen.width + g.setColor(b.color) + g.fillRect(0, b.top, width, b.top + b.thickness) +} + +const clearScreen = function () { + g.setColor(0) + const timeTop = settings.time.middle - (settings.time.size * 4) + g.fillRect(0, timeTop, screen.width, screen.height) +} + +let lastSeconds, tTick +const tick = function () { + g.reset() + const date = new Date() + const seconds = date.getSeconds() + if (lastSeconds > seconds) { + // new minute + clearScreen() + drawDateTime(date) + } + // the bar only gets larger, so drawing on top of the previous one is fine + drawBar(date) + lastSeconds = seconds + // schedule next update + const millis = date.getMilliseconds() + tTick = setTimeout(tick, 1000-millis) +} + +const start = function () { + lastSeconds = 99 // force redraw + tick() +} +const stop = function () { + if (tTick) { + clearTimeout(tTick) + tTick = undefined + } +} + +// clean app screen +g.clear() +Bangle.loadWidgets() +Bangle.drawWidgets() +// Show launcher when button pressed +Bangle.setUI("clock"); + +Bangle.on('lcdPower', function (on) { + if (on) { + start() + } else { + stop() + } +}) +start() diff --git a/apps/barclock/screenshot.png b/apps/barclock/screenshot.png new file mode 100644 index 000000000..d37ee9cae Binary files /dev/null and b/apps/barclock/screenshot.png differ diff --git a/apps/barclock/screenshot_pm.png b/apps/barclock/screenshot_pm.png new file mode 100644 index 000000000..a2a3f63fb Binary files /dev/null and b/apps/barclock/screenshot_pm.png differ diff --git a/apps/batclock/ChangeLog b/apps/batclock/ChangeLog index 5d221b4c4..e6e21b146 100644 --- a/apps/batclock/ChangeLog +++ b/apps/batclock/ChangeLog @@ -1 +1,2 @@ 0.01: App Created! +0.02: Update to use Bangle.setUI instead of setWatch diff --git a/apps/batclock/bat-clock.app.js b/apps/batclock/bat-clock.app.js index abb5fbd3a..31b8f5b9b 100644 --- a/apps/batclock/bat-clock.app.js +++ b/apps/batclock/bat-clock.app.js @@ -256,8 +256,5 @@ Bangle.drawWidgets(); timeInterval = setInterval(showTime, 1000); showTime(); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { - repeat: false, - edge: "falling" -}); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/bclock/ChangeLog b/apps/bclock/ChangeLog index 7819dbe2a..5b2cf598c 100644 --- a/apps/bclock/ChangeLog +++ b/apps/bclock/ChangeLog @@ -1 +1,2 @@ 0.02: Modified for use with new bootloader and firmware +0.03: Update to use Bangle.setUI instead of setWatch diff --git a/apps/bclock/clock-binary.js b/apps/bclock/clock-binary.js index 833aa00f6..fdf945ee6 100644 --- a/apps/bclock/clock-binary.js +++ b/apps/bclock/clock-binary.js @@ -105,5 +105,5 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); setInterval(() => { drawClock(); }, 1000); drawClock(); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/beebclock/ChangeLog b/apps/beebclock/ChangeLog index ac1c58c29..b9a4dacec 100644 --- a/apps/beebclock/ChangeLog +++ b/apps/beebclock/ChangeLog @@ -1,3 +1,4 @@ 0.01: Initial commit. Not very efficient, and widgets not working for some reason. 0.02: Fixes; widget support 0.03: Remove hardcoded hour buzz (you can install widchime if you miss it) +0.04: Update to use Bangle.setUI instead of setWatch diff --git a/apps/beebclock/beebclock.js b/apps/beebclock/beebclock.js index bbf65697f..2f7c48c36 100644 --- a/apps/beebclock/beebclock.js +++ b/apps/beebclock/beebclock.js @@ -51,341 +51,332 @@ Graphics.prototype.drawRotLine = function (sina, cosa, cx, cy, r1, r2) { ); }; +// Display modes +// +// 0: full-screen +// 1: with widgets +// 2: centred on Bangle (v.1), no widgets or time/date +// 3: centred with time above +// 4: centred with date above +// 5: centred with time and date above +let mode; -(function(g) { - // Display modes - // - // 0: full-screen - // 1: with widgets - // 2: centred on Bangle (v.1), no widgets or time/date - // 3: centred with time above - // 4: centred with date above - // 5: centred with time and date above - let mode; +// R1, R2: Outer and inner radii of hour marks +// RC1, RC2: Outer and inner radii of hub +// CX, CY: Centre location, relative to buffer (not screen, necessarily) +// HW2, MW2: Half-width of hour and minute hand +// HR, MR: Length of hour and minute hand, relative to CX,CY +// M: Half-width of gap in hour marks +// HSCALE: Half-width of hour mark as function(0 { + const fw = R1 * 2; + const fh = R1 * 2; + const fw2 = R1; + const fh2 = R1; + let hs = []; + + // Wipe the image and start with white + G.clear(); + G.setColor(1,1,1); + + // Draw the hour marks. for (let h=1; h<=12; h++) { - const a = Math.PI * h / 6; - ss[h] = Math.sin(a); - cs[h] = Math.cos(a); + hs[h] = HSCALE(h); + G.fillRotRect(ss[h], cs[h], CX, CY, -hs[h], hs[h], R2, R1); + } - // Draw the face with hour and minute hand. Ideally, we'd separate - // the face from the hands and double-buffer, but memory is limited, - // so we buffer once and minute, and draw the second hand dynamically - // (with a bit of flicker) - const drawFace = (G) => { - const fw = R1 * 2; - const fh = R1 * 2; - const fw2 = R1; - const fh2 = R1; - let hs = []; + // Draw the hub + G.fillCircle(CX, CY, RC1); - // Wipe the image and start with white - G.clear(); - G.setColor(1,1,1); + // Black + G.setColor(0,0,0); - // Draw the hour marks. - for (let h=1; h<=12; h++) { - hs[h] = HSCALE(h); - G.fillRotRect(ss[h], cs[h], CX, CY, -hs[h], hs[h], R2, R1); + // Clear the centre of the hub + G.fillCircle(CX, CY, RC2); + // Draw the gap in the hour marks + for (let h=1; h<=12; h++) { + G.fillRotRect(ss[h], cs[h], CX, CY, -M, M, R2-1, R1+1); + } + + // Back to white for future draw operations + G.setColor(1,1,1); + + // While the buffer remains full-screen, we may trim out the + // bottom of the image so we can shift the whole thing down for + // widgets. + const img = {width:GW,height:GH-TM,buffer:G.buffer}; + return img; +}; + +let hours, minutes, seconds, date; + +// Schedule event for calling at the start of the next second +const inOneSecond = (cb) => { + let now = new Date(); + clearTimeout(); + setTimeout(cb, 1000 - now.getMilliseconds()); +}; + +// Schedule event for calling at the start of the next minute +const inOneMinute = (cb) => { + let now = new Date(); + clearTimeout(); + setTimeout(cb, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds())); +}; + +// Draw a fat hour/minute hand +const drawHand = (G, a, w2, r1, r2) => + G.fillRotRect(Math.sin(a), Math.cos(a), CX, CY, -w2, w2, r1, r2); + +// Redraw function +const drawAll = (force) => { + let now = new Date(); + + if (!faceImg) force = true; + + let face_changed = force; + let date_changed = false; + + tmp = hours; + hours = now.getHours(); + if (tmp !== hours) + face_changed = true; + + tmp = minutes; + minutes = now.getMinutes(); + if (tmp !== minutes) + face_changed = true; + + // If the face has been updated and/or needs a redraw, + // face_changed is true. + + let time_changed = face_changed; + + // If the screen needs an update, regardless of whether the face + // needs a redraw, time_changed is true. + + if (with_seconds) { + // If we're going by second, we always need an update. + seconds = now.getSeconds(); + time_changed = true; + } + + if (with_digital_date) { + // See if the date has changed. If it has, then we need a + // full-blown redraw of the screen and the face, plus text. + tmp = date; + date = now.getDate(); + if (tmp !== date) { + date_changed = true; + face_changed = true; // Should have changed anyway with hour/minute rollover + } + } + + if (face_changed) { + // Redraw the face and hands onto the buffer G1. + faceImg = drawFace(G1); + drawHand(G1, Math.PI*hours/6, HW2, RC1, HR); + drawHand(G1, Math.PI*minutes/30, MW2, RC1, MR); + } + + // Has the time updated? If so, we'll need to draw something. + if (time_changed) { + + // Are we adding text? + if (with_digital_date || with_digital_time) { + + // Construct the date/time text to add above the face + let d = now.toString(); + let da = d.toString().split(" "); + let txt; + + if (with_digital_time) { + txt = da[4].substr(0, 5); + if (with_digital_date) + G1.drawStringDH(txt+',', 24, 0, 'L', GW); + else + G1.drawStringDH(txt, 0, 0, 'C', GW); + } + + if (with_digital_date) { + let txt = [da[0], da[1], da[2]].join(" "); + if (with_digital_time) + G1.drawStringDH(txt, -24, 0, 'R', GW); + else + G1.drawStringDH(txt, 0, 0, 'C', GW); + } } - // Draw the hub - G.fillCircle(CX, CY, RC1); - - // Black - G.setColor(0,0,0); - - // Clear the centre of the hub - G.fillCircle(CX, CY, RC2); - - // Draw the gap in the hour marks - for (let h=1; h<=12; h++) { - G.fillRotRect(ss[h], cs[h], CX, CY, -M, M, R2-1, R1+1); - } - - // Back to white for future draw operations - G.setColor(1,1,1); - - // While the buffer remains full-screen, we may trim out the - // bottom of the image so we can shift the whole thing down for - // widgets. - const img = {width:GW,height:GH-TM,buffer:G.buffer}; - return img; - }; - - let hours, minutes, seconds, date; - - // Schedule event for calling at the start of the next second - const inOneSecond = (cb) => { - let now = new Date(); - clearTimeout(); - setTimeout(cb, 1000 - now.getMilliseconds()); - }; - - // Schedule event for calling at the start of the next minute - const inOneMinute = (cb) => { - let now = new Date(); - clearTimeout(); - setTimeout(cb, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds())); - }; - - // Draw a fat hour/minute hand - const drawHand = (G, a, w2, r1, r2) => - G.fillRotRect(Math.sin(a), Math.cos(a), CX, CY, -w2, w2, r1, r2); - - // Redraw function - const drawAll = (force) => { - let now = new Date(); - - if (!faceImg) force = true; - - let face_changed = force; - let date_changed = false; - - tmp = hours; - hours = now.getHours(); - if (tmp !== hours) - face_changed = true; - - tmp = minutes; - minutes = now.getMinutes(); - if (tmp !== minutes) - face_changed = true; - - // If the face has been updated and/or needs a redraw, - // face_changed is true. - - let time_changed = face_changed; - - // If the screen needs an update, regardless of whether the face - // needs a redraw, time_changed is true. + // If the time has updated, we need to _at least_ draw the + // image to the screen. + g.setColor(1,1,1); + g.drawImage({width:GW, + height:GH-TM, + buffer:G1.buffer}, 0, TM); + // and possibly add the second hand if (with_seconds) { - // If we're going by second, we always need an update. - seconds = now.getSeconds(); - time_changed = true; + let a = 2.0 * Math.PI * seconds / 60.0; + g.drawRotLine(Math.sin(a), Math.cos(a), CX, CY+TM, RC1, R1); } - if (with_digital_date) { - // See if the date has changed. If it has, then we need a - // full-blown redraw of the screen and the face, plus text. - tmp = date; - date = now.getDate(); - if (tmp !== date) { - date_changed = true; - face_changed = true; // Should have changed anyway with hour/minute rollover - } - } + // And draw widgets if we're in that mode + if (with_widgets) + Bangle.drawWidgets(); + } - if (face_changed) { - // Redraw the face and hands onto the buffer G1. - faceImg = drawFace(G1); - drawHand(G1, Math.PI*hours/6, HW2, RC1, HR); - drawHand(G1, Math.PI*minutes/30, MW2, RC1, MR); - } + // Schedule to repeat this. A `setTimeout(1000)` isn't good + // enough, as all the above might've taken some milliseconds and + // we don't want to drift. + if (with_seconds) + inOneSecond(drawAll); + else + inOneMinute(drawAll); +}; - // Has the time updated? If so, we'll need to draw something. - if (time_changed) { +const setButtons = () => { + // Show launcher when button pressed + Bangle.setUI("clockupdown", btn=> { + if (btn==0) changeSeconds(); + if (btn==1) { ++mode; setMode(); drawAll(true); } + }); +}; - // Are we adding text? - if (with_digital_date || with_digital_time) { +// Load display parameters based on `mode` +const setMode = () => { + // Normalize mode to 0 <= mode <= 5 + mode = (6+mode) % 6; - // Construct the date/time text to add above the face - let d = now.toString(); - let da = d.toString().split(" "); - let txt; + // [R1, R2, RC1, RC2, HW2, MW3, HR, MR, M, HSCALE] = + const scales = [ + [120, 84, 17, 12.4, 4.6, 2.2, 8, 2, 1, h => (3.0 + Math.ceil(h/1.5)) ], + [102, 70, 14.6, 10.7, 3.88, 1.8, 8, 2, 1, h => (2.4 + Math.ceil(h/1.6)) ], + ]; - if (with_digital_time) { - txt = da[4].substr(0, 5); - if (with_digital_date) - G1.drawStringDH(txt+',', 24, 0, 'L', GW); - else - G1.drawStringDH(txt, 0, 0, 'C', GW); - } + if (mode < 3) { + // Face without time/date text. Might have widgets though. + with_digital_time = with_digital_date = false; + with_widgets = (mode == 1); + } + else { + // Face with time/date text, but no widgets + with_digital_time = (mode-2)&1; + with_digital_date = (mode-2)&2; + with_widgets = false; + } - if (with_digital_date) { - let txt = [da[0], da[1], da[2]].join(" "); - if (with_digital_time) - G1.drawStringDH(txt, -24, 0, 'R', GW); - else - G1.drawStringDH(txt, 0, 0, 'C', GW); - } - } + // Destructure the array to the global display parameters + let arr = scales[mode > 0 ? 1 : 0]; + R1 = arr[0]; + R2 = arr[1]; + RC1 = arr[2]; + RC2 = arr[3]; + HW2 = arr[4]; + MW2 = arr[5]; + HR = R2 - arr[6]; + MR = R1 - arr[7]; + M = arr[8]; + HSCALE = arr[9]; + TM = with_widgets ? 36 : 0; - // If the time has updated, we need to _at least_ draw the - // image to the screen. - g.setColor(1,1,1); - g.drawImage({width:GW, - height:GH-TM, - buffer:G1.buffer}, 0, TM); + CX = GW/2; + CY = R1; - // and possibly add the second hand - if (with_seconds) { - let a = 2.0 * Math.PI * seconds / 60.0; - g.drawRotLine(Math.sin(a), Math.cos(a), CX, CY+TM, RC1, R1); - } + // If we're in the small-face + text regime, we're going to buffer + // the full screen but draw the clock face further down to give + // space for the text. + // + // Compare with modes 0 (full-screen) and 1 (with_widgets==true) + // where the face is drawn at the top of the buffer, but drawn + // lower down the screen (so CY doesn't move) + if (mode > 1) { + CY += 36; + } - // And draw widgets if we're in that mode - if (with_widgets) - Bangle.drawWidgets(); - } + // We only don't bother redrawing the face from modes 2 to 5, as + // they're the same. + if (!faceImg || mode<3) { + faceImg = undefined; + } - // Schedule to repeat this. A `setTimeout(1000)` isn't good - // enough, as all the above might've taken some milliseconds and - // we don't want to drift. - if (with_seconds) - inOneSecond(drawAll); - else - inOneMinute(drawAll); - }; - - const setButtons = () => { - const opts = { repeat: true, edge:'rising', debounce:30}; - - // BTN1: enable/disable second hand - setWatch(changeSeconds, BTN1, opts); - - // BTN2: return to launcher - setWatch(Bangle.showLauncher, BTN2, { repeat:false, edge:'falling' }); - - // BTN3: change display mode - setWatch(function () { ++mode; setMode(); drawAll(true); }, BTN3, opts); - }; - - // Load display parameters based on `mode` - const setMode = () => { - // Normalize mode to 0 <= mode <= 5 - mode = (6+mode) % 6; - - // [R1, R2, RC1, RC2, HW2, MW3, HR, MR, M, HSCALE] = - const scales = [ - [120, 84, 17, 12.4, 4.6, 2.2, 8, 2, 1, h => (3.0 + Math.ceil(h/1.5)) ], - [102, 70, 14.6, 10.7, 3.88, 1.8, 8, 2, 1, h => (2.4 + Math.ceil(h/1.6)) ], - ]; - - if (mode < 3) { - // Face without time/date text. Might have widgets though. - with_digital_time = with_digital_date = false; - with_widgets = (mode == 1); - } - else { - // Face with time/date text, but no widgets - with_digital_time = (mode-2)&1; - with_digital_date = (mode-2)&2; - with_widgets = false; - } - - // Destructure the array to the global display parameters - let arr = scales[mode > 0 ? 1 : 0]; - R1 = arr[0]; - R2 = arr[1]; - RC1 = arr[2]; - RC2 = arr[3]; - HW2 = arr[4]; - MW2 = arr[5]; - HR = R2 - arr[6]; - MR = R1 - arr[7]; - M = arr[8]; - HSCALE = arr[9]; - TM = with_widgets ? 36 : 0; - - CX = GW/2; - CY = R1; - - // If we're in the small-face + text regime, we're going to buffer - // the full screen but draw the clock face further down to give - // space for the text. - // - // Compare with modes 0 (full-screen) and 1 (with_widgets==true) - // where the face is drawn at the top of the buffer, but drawn - // lower down the screen (so CY doesn't move) - if (mode > 1) { - CY += 36; - } - - // We only don't bother redrawing the face from modes 2 to 5, as - // they're the same. - if (!faceImg || mode<3) { - faceImg = undefined; - } - - // Store the settings for next time - try { - storage.writeJSON(filename, [mode,with_seconds]); - } catch (e) { - console.log(e); - } - - // Clear the screen: we need to make sure all parts are cleaned off. - g.clear(); - }; - - const changeSeconds = () => { - with_seconds = !with_seconds; - drawAll(true); - }; - - Bangle.loadWidgets(); - - // Restore mode + // Store the settings for next time try { - conf = storage.readJSON(filename); - mode = conf[0]; - with_seconds = conf[1]; + storage.writeJSON(filename, [mode,with_seconds]); } catch (e) { console.log(e); - mode = 1; } - setButtons(); - setMode(); - drawAll(); + // Clear the screen: we need to make sure all parts are cleaned off. + g.clear(); +}; - Bangle.on('lcdPower', (on) => { - if (on) { - Bangle.loadWidgets(); - Bangle.drawWidgets(); - drawAll(); - } else { - clearTimeout(); - } - }); +const changeSeconds = () => { + with_seconds = !with_seconds; + drawAll(true); +}; -})(g); +Bangle.loadWidgets(); + +// Restore mode +try { + conf = storage.readJSON(filename); + mode = conf[0]; + with_seconds = conf[1]; +} catch (e) { + console.log(e); + mode = 1; +} + +setButtons(); +setMode(); +drawAll(); + +Bangle.on('lcdPower', (on) => { + if (on) { + Bangle.loadWidgets(); + Bangle.drawWidgets(); + drawAll(); + } else { + clearTimeout(); + } +}); diff --git a/apps/berlinc/ChangeLog b/apps/berlinc/ChangeLog index a33332bc4..65ed0505f 100644 --- a/apps/berlinc/ChangeLog +++ b/apps/berlinc/ChangeLog @@ -1,2 +1,3 @@ 0.02: Modified for use with new bootloader and firmware 0.03: Shrinked size to avoid cut-off edges on the physical device. BTN3: show date. BTN1: show time in decimal. +0.04: Update to use Bangle.setUI instead of setWatch diff --git a/apps/berlinc/berlin-clock.js b/apps/berlinc/berlin-clock.js index 3950147b8..144fa5ba7 100644 --- a/apps/berlinc/berlin-clock.js +++ b/apps/berlinc/berlin-clock.js @@ -16,7 +16,7 @@ time_digit = []; function drawBerlinClock() { g.clear(); var now = new Date(); - + // show date below the clock if (show_date) { var yr = now.getFullYear(); @@ -28,7 +28,7 @@ function drawBerlinClock() { g.setFontAlign(-1,-1); g.drawString(dateString, ( g.getWidth() - strWidth ) / 2, height + offset + 4); } - + rowlights[0] = Math.floor(now.getHours() / 5); rowlights[1] = now.getHours() % 5; rowlights[2] = Math.floor(now.getMinutes() / 5); @@ -62,7 +62,7 @@ function drawBerlinClock() { } else { g.setColor(1, 0, 0); } - g.fillRect(x1 + 2, y1 + 2, x2 - 2, y2 - 2); + g.fillRect(x1 + 2, y1 + 2, x2 - 2, y2 - 2); } if (row == 3 && show_time) { g.setColor(1,1,1); @@ -100,9 +100,11 @@ g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); drawBerlinClock(); -// Toggle date display, when BTN3 is pressed -setWatch(toggleTime,BTN1, { repeat : true, edge: "falling"}); -// Toggle date display, when BTN3 is pressed -setWatch(toggleDate,BTN3, { repeat : true, edge: "falling"}); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); +if (BTN3) { + // Toggle date display, when BTN3 is pressed + setWatch(toggleTime,BTN1, { repeat : true, edge: "falling"}); + // Toggle date display, when BTN3 is pressed + setWatch(toggleDate,BTN3, { repeat : true, edge: "falling"}); +} +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/binclock/ChangeLog b/apps/binclock/ChangeLog index 2378e52f8..dc4ed8308 100644 --- a/apps/binclock/ChangeLog +++ b/apps/binclock/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Fixed bug where screen didn't clear so incorrect time displayed. +0.03: Update to use Bangle.setUI instead of setWatch diff --git a/apps/binclock/app.js b/apps/binclock/app.js index 7808dfe45..f8cbe8dd5 100644 --- a/apps/binclock/app.js +++ b/apps/binclock/app.js @@ -23,17 +23,17 @@ function drawTime(d) { } function updateHourArray(hours){ - + var j; for(j=0;j 15){ hourLED[0] = 1; hours = hours - 16; @@ -53,9 +53,9 @@ function updateHourArray(hours){ if(hours > 0){ hourLED[4] = 1; } - + return hourLED; - + } function updateMinuteArray(minutes){ @@ -63,12 +63,12 @@ function updateMinuteArray(minutes){ for(j=0;j 31){ minuteLED[0] = 1; minutes = minutes - 32; @@ -92,20 +92,20 @@ function updateMinuteArray(minutes){ if(minutes > 0){ minuteLED[5] = 1; } - + return minuteLED; - + } function draw(){ - + // work out how to display the current time var d = new Date(); var h = d.getHours(), m = d.getMinutes(); - + updateHourArray(h); updateMinuteArray(m); - + var i; //Draw hour circles for(i=0; i{ // Load widgets Bangle.loadWidgets(); Bangle.drawWidgets(); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); -setWatch(function() { +// Show launcher when button pressed +Bangle.setUI("clockupdown", btn=>{ + if (btn!=1) return; if(displayTime == 0){ displayTime = 1; } else{ - displayTime = 0; + displayTime = 0; } -}, BTN, {edge:"rising", debounce:50, repeat:true}); +}); diff --git a/apps/blobclk/ChangeLog b/apps/blobclk/ChangeLog index 10983d7e1..9c4ef5b7b 100644 --- a/apps/blobclk/ChangeLog +++ b/apps/blobclk/ChangeLog @@ -4,3 +4,4 @@ 0.03: Modified for use with new bootloader and firmware 0.04: Modified to account for changes in the behavior of Graphics.fillPoly 0.05: Slight increase to draw speed after LCD on +0.06: Update to use Bangle.setUI instead of setWatch, allow themes and different size screens diff --git a/apps/blobclk/clock-blob.js b/apps/blobclk/clock-blob.js index 9b68bd4bd..c84b8a1e6 100644 --- a/apps/blobclk/clock-blob.js +++ b/apps/blobclk/clock-blob.js @@ -1,4 +1,6 @@ -const buf = Graphics.createArrayBuffer(144,200,1,{msb:true}); +let big = g.getHeight() > 200; +const buf = Graphics.createArrayBuffer(big ? 144 : 120, big ? 180 : 150,1,{msb:true}); +// TODO: convert these to Polys -> much faster and cleaner! const NUMBERS = [ [1,1,1,1,3,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1],//0 [0,1,1,1,3,0,0,1,1,1,0,0,1,1,1,0,0,1,1,1,0,0,1,1,1],//1 @@ -14,8 +16,10 @@ const NUMBERS = [ let intervalRef = null; let digits = [-1,-1,-1,-1,-1,-1]; function flip() { - g.setColor(1,1,1); - g.drawImage({width:buf.getWidth(),height:buf.getHeight(),buffer:buf.buffer},55,26); + g.reset(); + g.drawImage({width:buf.getWidth(),height:buf.getHeight(),buffer:buf.buffer}, + (g.getWidth() - buf.getWidth())/2, + 26 + (g.getHeight() - (buf.getHeight()+24))/2); } function drawPixel(ox,oy,x,y,r,p) { let x1 = ox+x*(r*2); @@ -53,26 +57,31 @@ function redraw() { let newDigits = [Math.floor(hours/10),hours%10,Math.floor(mins/10),mins%10,Math.floor(secs/10),secs%10]; + let s = big?6:5; // size of main digits + let y2 = big?72:55; + let y3 = big?144:110; + + for (var p = 0;p<25;p++) { var px = p%5; var py = Math.floor(p/5); if (digits[0] === -1 || NUMBERS[newDigits[0]][p] !== NUMBERS[digits[0]][p] ) { - drawPixel(0,20,px,py,6,NUMBERS[newDigits[0]][p]); + drawPixel(0,0,px,py,s,NUMBERS[newDigits[0]][p]); } if (digits[1] === -1 || NUMBERS[newDigits[1]][p] !== NUMBERS[digits[1]][p] ) { - drawPixel(78,20,px,py,6,NUMBERS[newDigits[1]][p]); + drawPixel(13*s,0,px,py,s,NUMBERS[newDigits[1]][p]); } if (digits[2] === -1 || NUMBERS[newDigits[2]][p] !== NUMBERS[digits[2]][p] ) { - drawPixel(0,92,px,py,6,NUMBERS[newDigits[2]][p]); + drawPixel(0,y2,px,py,s,NUMBERS[newDigits[2]][p]); } if (digits[3] === -1 || NUMBERS[newDigits[3]][p] !== NUMBERS[digits[3]][p] ) { - drawPixel(78,92,px,py,6,NUMBERS[newDigits[3]][p]); + drawPixel(13*s,y2,px,py,s,NUMBERS[newDigits[3]][p]); } if (digits[4] === -1 || NUMBERS[newDigits[4]][p] !== NUMBERS[digits[4]][p] ) { - drawPixel(69,164,px,py,3,NUMBERS[newDigits[4]][p]); + drawPixel(17*s - 3*12,y3,px,py,3,NUMBERS[newDigits[4]][p]); } if (digits[5] === -1 || NUMBERS[newDigits[5]][p] !== NUMBERS[digits[5]][p] ) { - drawPixel(108,164,px,py,3,NUMBERS[newDigits[5]][p]); + drawPixel(17*s,y3,px,py,3,NUMBERS[newDigits[5]][p]); } } digits = newDigits; @@ -99,5 +108,5 @@ Bangle.on('lcdPower',function(on) { clearTimers(); } }); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/boldclk/ChangeLog b/apps/boldclk/ChangeLog index 0d02bf644..f5d1af878 100644 --- a/apps/boldclk/ChangeLog +++ b/apps/boldclk/ChangeLog @@ -1,2 +1,3 @@ 0.02: Modified for use with new bootloader and firmware 0.03: Tweak for more efficient rendering, and firmware 2v06 +0.04: Work with themes, smaller screens diff --git a/apps/boldclk/bold_clock.js b/apps/boldclk/bold_clock.js index b7eaa8968..191b6ec4f 100644 --- a/apps/boldclk/bold_clock.js +++ b/apps/boldclk/bold_clock.js @@ -12,9 +12,9 @@ var minute_hand = { //g.fillRect(0,24,239,239); // Apps area let intervalRef = null; const p180 = Math.PI/180; -const clock_center = {x:Math.floor((240-1)/2), y:24+Math.floor((239-24)/2)}; +const clock_center = {x:Math.floor((g.getWidth()-1)/2), y:24+Math.floor((g.getHeight()-25)/2)}; // ={ x: 119, y: 131 } -const radius = Math.floor((239-24+1)/2); // =108 +const radius = Math.floor((g.getWidth()-24+1)/2); // =108 let tick0 = Graphics.createArrayBuffer(30,8,1,{msb:true}); tick0.fillRect(0,0,tick0.getWidth()-1, tick0.getHeight()-1); @@ -60,18 +60,15 @@ function hour_angle(date){ function draw_clock(){ //console.log("draw_clock"); let date = new Date(); - //g.clear(); - g.setBgColor(0,0,0); - g.setColor(0,0,0); - g.fillRect(0,24,239,239); // clear app area - g.setColor(1,1,1); + g.reset(); + g.clearRect(0,24,239,239); // clear app area // draw cross lines for testing // g.setColor(1,0,0); // g.drawLine(clock_center.x - radius, clock_center.y, clock_center.x + radius, clock_center.y); // g.drawLine(clock_center.x, clock_center.y - radius, clock_center.x, clock_center.y + radius); - g.setColor(1,1,1); + g.setColor(g.theme.fg); let ticks = [0, 90, 180, 270]; ticks.forEach((item)=>{ let agl = item+180; @@ -87,13 +84,13 @@ function draw_clock(){ let minute_agl = minute_angle(date); g.drawImage(hour_hand, hour_pos_x(hour_agl), hour_pos_y(hour_agl), {rotate:hour_agl*p180}); // g.drawImage(minute_hand, minute_pos_x(minute_agl), minute_pos_y(minute_agl), {rotate:minute_agl*p180}); // - g.setColor(1,1,1); + g.setColor(g.theme.fg); g.fillCircle(clock_center.x, clock_center.y, 6); - g.setColor(0,0,0); + g.setColor(g.theme.bg); g.fillCircle(clock_center.x, clock_center.y, 3); // draw minute ticks. Takes long time to draw! - g.setColor(1,1,1); + g.setColor(g.theme.fg); for (var i=0; i<60; i++){ let agl = i*6+180; g.drawImage(tick1.asImage(), rotate_around_x(big_wheel_x(i*6), agl, tick1), rotate_around_y(big_wheel_y(i*6), agl, tick1), {rotate:agl*p180}); @@ -141,5 +138,5 @@ g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); startTimers(); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index 7e9fd4a81..ccf97a9eb 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -21,3 +21,8 @@ 0.20: Allow Gadgetbridge to work even with programmable:off 0.21: Handle echo off char from Gadgetbridge app when programmable:off (fix #558) 0.22: Stop LCD timeout being disabled on first run (when there is no settings.json) +0.23: Move to a precalculated .boot0 file which should speed up load time +0.24: Add Bangle.setUI polyfill +0.25: Fix error in 'no clock app' message +0.26: Remove buzz in setUI polyfill (#750) +0.27: Update polyfill for most recent changes diff --git a/apps/boot/boot0.js b/apps/boot/boot0.js index 550513b11..3e567d9b8 100644 --- a/apps/boot/boot0.js +++ b/apps/boot/boot0.js @@ -1,68 +1,2 @@ -// This ALWAYS runs at boot -E.setFlags({pretokenise:1}); -// Load settings... -var s = require('Storage').readJSON('setting.json',1)||{}; -if (s.ble!==false) { - if (s.HID) { // Human interface device - if (s.HID=="joy") Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA=")); - else if (s.HID=="kb") Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA==")); - else /*kbmedia*/Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA==")); - NRF.setServices({}, {uart:true, hid:Bangle.HID}); - } -} -if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth - if (s.log) Terminal.setConsole(true); // if showing debug, force REPL onto terminal - else E.setConsole(null,{force:true}); // on new (2v05+) firmware we have E.setConsole which allows a 'null' console - /* If not programmable add our own handler for Bluetooth data - to allow Gadgetbridge commands to be received*/ - Bluetooth.line=""; - Bluetooth.on('data',function(d) { - var l = (Bluetooth.line + d).split("\n"); - Bluetooth.line = l.pop(); - l.forEach(n=>Bluetooth.emit("line",n)); - }); - Bluetooth.on('line',function(l) { - if (l.startsWith('\x10')) l=l.slice(1); - if (l.startsWith('GB({') && l.endsWith('})') && global.GB) - try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {} - }); -} else { - if (s.log && !NRF.getSecurityStatus().connected) Terminal.setConsole(); // if showing debug, put REPL on terminal (until connection) - else Bluetooth.setConsole(true); // else if no debug, force REPL to Bluetooth -} -// we just reset, so BLE should be on. -// Don't disconnect if something is already connected to us -if (s.ble===false && !NRF.getSecurityStatus().connected) NRF.sleep(); -// Set time, vibrate, beep, etc -if (!Bangle.F_BEEPSET) { - if (!s.vibrate) Bangle.buzz=Promise.resolve; - if (s.beep===false) Bangle.beep=Promise.resolve; - else if (s.beep=="vib") Bangle.beep = function (time, freq) { - return new Promise(function(resolve) { - if ((0|freq)<=0) freq=4000; - if ((0|time)<=0) time=200; - if (time>5000) time=5000; - analogWrite(D13,0.1,{freq:freq}); - setTimeout(function() { - digitalWrite(D13,0); - resolve(); - }, time); - }); - }; -} -if (s.timeout!==undefined) Bangle.setLCDTimeout(s.timeout); -if (!s.timeout) Bangle.setLCDPower(1); -E.setTimeZone(s.timezone); -delete s; -// Draw out of memory errors onto the screen -E.on('errorFlag', function(errorFlags) { - g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip(); - print("Interpreter error:", errorFlags); - E.getErrorFlags(); // clear flags so we get called next time -}); -// stop users doing bad things! -global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); } -// Load *.boot.js files -require('Storage').list(/\.boot\.js/).forEach(bootFile=>{ - eval(require('Storage').read(bootFile)); -}); +// Initially this runs and rewrites itself +eval(require('Storage').read('bootupdate.js')); diff --git a/apps/boot/bootloader.js b/apps/boot/bootloader.js index df3718dcc..138258c5a 100644 --- a/apps/boot/bootloader.js +++ b/apps/boot/bootloader.js @@ -14,11 +14,7 @@ if (!clockApp) { if (clockApp) clockApp = require("Storage").read(clockApp.src); } -if (!clockApp) clockApp=`E.showMessage("No Clock Found"); -setWatch(() => { - Bangle.showLauncher(); -}, BTN2, {repeat:false,edge:"falling"});) -`; +if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, BTN2, {repeat:false,edge:"falling"});`; // check to see if our clock is wrong - if it is use GPS time if ((new Date()).getFullYear()<2000) { E.showMessage("Searching for\nGPS time"); diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js new file mode 100644 index 000000000..014edd552 --- /dev/null +++ b/apps/boot/bootupdate.js @@ -0,0 +1,143 @@ +/* This rewrites boot0.js based on current settings. If settings changed then it +recalculates, but this avoids us doing a whole bunch of reconfiguration most +of the time. */ +E.showMessage("Updating boot0..."); +var s = require('Storage').readJSON('setting.json',1)||{}; +var isB2 = process.env.HWVERSION; // Is Bangle.js 2 +var boot = ""; +var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/)); +boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))!=${CRC}) { eval(require('Storage').read('bootupdate.js'));} else {\n`; +boot += `E.setFlags({pretokenise:1});\n`; +if (s.ble!==false) { + if (s.HID) { // Human interface device + if (s.HID=="joy") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));`; + else if (s.HID=="kb") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA=="));` + else /*kbmedia*/boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));`; + boot += `NRF.setServices({}, {uart:true, hid:Bangle.HID});\n`; + } +} +if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth + if (s.log) boot += `Terminal.setConsole(true);\n`; // if showing debug, force REPL onto terminal + else boot += `E.setConsole(null,{force:true});\n`; // on new (2v05+) firmware we have E.setConsole which allows a 'null' console + /* If not programmable add our own handler for Bluetooth data + to allow Gadgetbridge commands to be received*/ + boot += ` +Bluetooth.line=""; +Bluetooth.on('data',function(d) { + var l = (Bluetooth.line + d).split("\n"); + Bluetooth.line = l.pop(); + l.forEach(n=>Bluetooth.emit("line",n)); +}); +Bluetooth.on('line',function(l) { + if (l.startsWith('\x10')) l=l.slice(1); + if (l.startsWith('GB({') && l.endsWith('})') && global.GB) + try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {} +});\n`; +} else { + if (s.log) boot += `if (!NRF.getSecurityStatus().connected) Terminal.setConsole();\n`; // if showing debug, put REPL on terminal (until connection) + else boot += `Bluetooth.setConsole(true);\n`; // else if no debug, force REPL to Bluetooth +} +// we just reset, so BLE should be on. +// Don't disconnect if something is already connected to us +if (s.ble===false) boot += `if (!NRF.getSecurityStatus().connected) NRF.sleep();\n`; +// Set time +if (s.timeout!==undefined) boot += `Bangle.setLCDTimeout(${s.timeout});\n`; +if (!s.timeout) boot += `Bangle.setLCDPower(1);\n`; +boot += `E.setTimeZone(${s.timezone});`; +// Set vibrate, beep, etc IF on older firmwares +if (!Bangle.F_BEEPSET) { + if (!s.vibrate) boot += `Bangle.buzz=Promise.resolve;\n` + if (s.beep===false) boot += `Bangle.beep=Promise.resolve;\n` + else if (s.beep=="vib") boot += `Bangle.beep = function (time, freq) { + return new Promise(function(resolve) { + if ((0|freq)<=0) freq=4000; + if ((0|time)<=0) time=200; + if (time>5000) time=5000; + analogWrite(D13,0.1,{freq:freq}); + setTimeout(function() { + digitalWrite(D13,0); + resolve(); + }, time); + }); + };\n`; +} +// Draw out of memory errors onto the screen +boot += `E.on('errorFlag', function(errorFlags) { + g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip(); + print("Interpreter error:", errorFlags); + E.getErrorFlags(); // clear flags so we get called next time +});\n`; +// stop users doing bad things! +if (global.save) boot += `global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }\n`; +// Apply any settings-specific stuff +if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`; +if (s.quiet && s.qmOptions) boot+=`Bangle.setOptions(${E.toJS(s.qmOptions)});\n`; +if (s.quiet && s.qmBrightness) { + if (s.qmBrightness!=1) boot+=`Bangle.setLCDBrightness(${s.qmBrightness});\n`; +} else { + if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`; +} +if (s.quiet && s.qmTimeout) boot+=`Bangle.setLCDTimeout(${s.qmTimeout});\n`; +if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${s.passkey}, mitm:1, display:1});\n`; +if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`; +// Pre-2v10 firmwares without a theme/setUI +if (!g.theme) { + boot += `g.theme={fg:-1,bg:0,fg2:-1,bg2:7,fgH:-1,bgH:0x02F7,dark:true};\n`; +} +if (!Bangle.setUI) { // assume this is just for F18 - Q3 should already have it + boot += `Bangle.setUI=function(mode, cb) { +if (Bangle.btnWatches) { + Bangle.btnWatches.forEach(clearWatch); + delete Bangle.btnWatches; +} +if (Bangle.swipeHandler) { + Bangle.removeListener("swipe", Bangle.swipeHandler); + delete Bangle.swipeHandler; +} +if (Bangle.touchandler) { + Bangle.removeListener("touch", Bangle.touchHandler); + delete Bangle.touchHandler; +} +if (!mode) return; +else if (mode=="updown") { + Bangle.btnWatches = [ + setWatch(function() { cb(-1); }, BTN1, {repeat:1}), + setWatch(function() { cb(1); }, BTN3, {repeat:1}), + setWatch(function() { cb(); }, BTN2, {repeat:1}) + ]; +} else if (mode=="leftright") { + Bangle.btnWatches = [ + setWatch(function() { cb(-1); }, BTN1, {repeat:1}), + setWatch(function() { cb(1); }, BTN3, {repeat:1}), + setWatch(function() { cb(); }, BTN2, {repeat:1}) + ]; + Bangle.swipeHandler = d => {cb(d);}; + Bangle.on("swipe", Bangle.swipeHandler); + Bangle.touchHandler = d => {cb();}; + Bangle.on("touch", Bangle.touchHandler); +} else if (mode=="clock") { + Bangle.CLOCK=1; + Bangle.btnWatches = [ + setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"falling"}) + ]; +} else if (mode=="clockupdown") { + Bangle.CLOCK=1; + Bangle.btnWatches = [ + setWatch(function() { cb(-1); }, BTN1, {repeat:1}), + setWatch(function() { cb(1); }, BTN3, {repeat:1}), + setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"falling"}) + ]; +} else + throw new Error("Unknown UI mode"); +};\n`; +} +// Append *.boot.js files +require('Storage').list(/\.boot\.js/).forEach(bootFile=>{ + boot += "//"+bootFile+"\n"+require('Storage').read(bootFile)+"\n"; +}); +boot += "}\n";// initial 'if' +var s = require('Storage').write('.boot0',boot); +delete boot; +E.showMessage("Reloading..."); +eval(require('Storage').read('.boot0')); +eval(require('Storage').read('.bootcde')); diff --git a/apps/buffgym/buffgym-scrn1.png b/apps/buffgym/buffgym-scrn1.png old mode 100755 new mode 100644 diff --git a/apps/buffgym/buffgym-scrn2.png b/apps/buffgym/buffgym-scrn2.png old mode 100755 new mode 100644 diff --git a/apps/buffgym/buffgym-scrn3.png b/apps/buffgym/buffgym-scrn3.png old mode 100755 new mode 100644 diff --git a/apps/buffgym/buffgym-scrn4.png b/apps/buffgym/buffgym-scrn4.png old mode 100755 new mode 100644 diff --git a/apps/buffgym/buffgym-scrn5.png b/apps/buffgym/buffgym-scrn5.png old mode 100755 new mode 100644 diff --git a/apps/buffgym/buffgym-scrn6.png b/apps/buffgym/buffgym-scrn6.png old mode 100755 new mode 100644 diff --git a/apps/buffgym/buffgym.app.js b/apps/buffgym/buffgym.app.js old mode 100755 new mode 100644 diff --git a/apps/buffgym/buffgym.png b/apps/buffgym/buffgym.png old mode 100755 new mode 100644 diff --git a/apps/calendar/ChangeLog b/apps/calendar/ChangeLog index 3cf79ffe8..8bafff34a 100644 --- a/apps/calendar/ChangeLog +++ b/apps/calendar/ChangeLog @@ -1 +1,2 @@ 0.01: Basic calendar +0.02: Make Bangle 2 compatible diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js index 720986162..6f3c33164 100644 --- a/apps/calendar/calendar.js +++ b/apps/calendar/calendar.js @@ -1,5 +1,6 @@ -const maxX = 240; -const maxY = 240; +const maxX = g.getWidth(); +const maxY = g.getHeight(); +const fontSize = g.getWidth()>200?2:1; const rowN = 7; const colN = 7; const headerH = maxY / 7; @@ -50,7 +51,7 @@ function drawCalendar(date) { 11: "December" }; g.setFontAlign(0, 0); - g.setFont("6x8", 2); + g.setFont("6x8", fontSize); g.setColor(white); g.drawString(`${monthMap[month]} ${year}`, maxX / 2, headerH / 2); g.drawPoly([10, headerH / 2, 20, 10, 20, headerH - 10], true); @@ -59,7 +60,7 @@ function drawCalendar(date) { true ); - g.setFont("6x8", 2); + g.setFont("6x8", fontSize); const dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]; dowLbls.forEach((lbl, i) => { g.drawString(lbl, i * colW + colW / 2, headerH + rowH / 2); @@ -135,26 +136,21 @@ const today = { }; drawCalendar(date); clearWatch(); -setWatch( - () => { - const month = date.getMonth(); - const prevMonth = month > 0 ? month - 1 : 11; +Bangle.on("touch",area=>{ + const month = date.getMonth(); + let prevMonth; + if (area==1) { + let prevMonth = month > 0 ? month - 1 : 11; if (prevMonth === 11) date.setFullYear(date.getFullYear() - 1); date.setMonth(prevMonth); - drawCalendar(date); - }, - BTN4, - { repeat: true } -); -setWatch( - () => { - const month = date.getMonth(); - const prevMonth = month < 11 ? month + 1 : 0; + } else { + let prevMonth = month < 11 ? month + 1 : 0; if (prevMonth === 0) date.setFullYear(date.getFullYear() + 1); date.setMonth(month + 1); - drawCalendar(date); - }, - BTN5, - { repeat: true } -); -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); + } + drawCalendar(date); +}); + +// Show launcher when button pressed +Bangle.setUI("clock"); // TODO: ideally don't set 'clock' mode +// No space for widgets! diff --git a/apps/cliock/ChangeLog b/apps/cliock/ChangeLog index 07b38e189..66ef62eae 100644 --- a/apps/cliock/ChangeLog +++ b/apps/cliock/ChangeLog @@ -1,5 +1,7 @@ 0.07: Submitted to App Loader -0.08: Fixes issue where face would redraw on wake leading to all memory being used and watch crashing. +0.08: Fixes issue where face would redraw on wake leading to all memory being used and watch crashing. 0.09: Add BTN1 status line with ID,Fw ver, mem %, battery % 0.10: Icon fixed for transparency 0.11: added Heart Rate Monitor status and ability to turn on/off +0.12: added support for different locales +0.13: Use setUI, work with smaller screens and themes diff --git a/apps/cliock/app.js b/apps/cliock/app.js index ca48bb26f..6853aaf6f 100644 --- a/apps/cliock/app.js +++ b/apps/cliock/app.js @@ -1,8 +1,8 @@ -var fontsize = 3; +var fontsize = g.getWidth()>200 ? 3 : 2; +var fontheight = 10*fontsize; var locale = require("locale"); var marginTop = 40; var flag = false; -var WeekDays = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; var hrtOn = false; var hrtStr = "Hrt: ??? bpm"; @@ -26,41 +26,37 @@ function drawAll(){ } function updateRest(now){ - let date = locale.date(now,false); - writeLine(WeekDays[now.getDay()],1); - writeLine(date,2); + writeLine(locale.dow(now),1); + writeLine(locale.date(now,1),2); drawInfo(5); } function updateTime(){ if (!Bangle.isLCDOn()) return; let now = new Date(); - let h = now.getHours(); - let m = now.getMinutes(); - h = h>=10?h:"0"+h; - m = m>=10?m:"0"+m; - writeLine(h+":"+m,0); + writeLine(locale.time(now,1),0); writeLine(flag?" ":"_",3); flag = !flag; if(now.getMinutes() == 0) updateRest(now); } function writeLineStart(line){ - g.drawString(">",4,marginTop+line*30); + g.drawString(">",4,marginTop+line*fontheight); } function writeLine(str,line){ + var y = marginTop+line*fontheight; g.setFont("6x8",fontsize); //g.setColor(0,1,0); - g.setColor(0,0x07E0,0); + g.setColor("#0f0"); g.setFontAlign(-1,-1); - g.clearRect(0,marginTop+line*30,((str.length+1)*20),marginTop+25+line*30); + g.clearRect(0,y,((str.length+1)*20),y+fontheight-1); writeLineStart(line); - g.drawString(str,25,marginTop+line*30); -} + g.drawString(str,25,y); +} function drawInfo(line) { let val; let str = ""; - let col = 0x07E0; // green + let col = "#0f0"; // green //console.log("drawInfo(), infoMode=" + infoMode + " funcMode=" + functionMode); @@ -68,15 +64,15 @@ function drawInfo(line) { case NONE_FN_MODE: break; case HRT_FN_MODE: - col = 0x07FF; // cyan + col = "#0ff"; // cyan str = "HRM: " + (hrtOn ? "ON" : "OFF"); drawModeLine(line,str,col); return; } - + switch(infoMode) { case NONE_MODE: - col = 0x0000; + col = "#fff"; str = ""; break; case HRT_MODE: @@ -106,10 +102,11 @@ function drawInfo(line) { function drawModeLine(line, str, col) { g.setColor(col); - g.fillRect(0, marginTop-3+line*30, 239, marginTop+25+line*30); - g.setColor(0,0,0); + var y = marginTop+line*fontheight; + g.fillRect(0, y, 239, y+fontheight-1); + g.setColor(0); g.setFontAlign(0, -1); - g.drawString(str, g.getWidth()/2, marginTop+line*30); + g.drawString(str, g.getWidth()/2, y); } function changeInfoMode() { @@ -166,7 +163,7 @@ function changeFunctionMode() { functionMode = NONE_FN_MODE; } //console.log(functionMode); - + } function stepsWidget() { @@ -191,10 +188,12 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); drawAll(); Bangle.on('lcdPower',function(on) { - if (on) - drawAll(); + if (on) drawAll(); }); var click = setInterval(updateTime, 1000); -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); -setWatch(() => { changeInfoMode(); drawAll(); }, BTN1, {repeat: true}); -setWatch(() => { changeFunctionMode(); drawAll(); }, BTN3, {repeat: true}); +// Show launcher when button pressed +Bangle.setUI("clockupdown", btn=>{ + if (btn==0) changeInfoMode(); + if (btn==1) changeFunctionMode(); + drawAll(); +}); diff --git a/apps/clock2x3/ChangeLog b/apps/clock2x3/ChangeLog index 88876affa..ef8057d6e 100644 --- a/apps/clock2x3/ChangeLog +++ b/apps/clock2x3/ChangeLog @@ -1,3 +1,4 @@ 0.02: Modified for use with new bootloader and firmware 0.03: Added 'reset' so we don't get the font color from widgets 0.04: Changed name from clck3x2 to clock2x3 +0.05: Use setUI, work with smaller screens and themes diff --git a/apps/clock2x3/clock2x3-app.js b/apps/clock2x3/clock2x3-app.js index 4caec28cb..bfe6a9ea8 100644 --- a/apps/clock2x3/clock2x3-app.js +++ b/apps/clock2x3/clock2x3-app.js @@ -1,8 +1,10 @@ + +const big = g.getWidth()>200; const ox=10; // x offset -const oy=80; -const pw=20; // pixel width -const ps=5; // pixel spacing -const ds=10; // digit spacing +const oy=big ? 80 : 70; +const pw=big ? 20 : 14; // pixel width +const ps=big ? 5 : 3; // pixel spacing +const ds=big ? 10 : 8; // digit spacing const ms=20; // middle space const x00=ox; // digit 0, pixel 0, x position @@ -90,7 +92,7 @@ Bangle.on('lcdPower', function(on){ } }); +// Show launcher when button pressed +Bangle.setUI("clock"); Bangle.loadWidgets(); drawTime(); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); diff --git a/apps/ctrclk/ChangeLog b/apps/ctrclk/ChangeLog index 7819dbe2a..e4659c1ae 100644 --- a/apps/ctrclk/ChangeLog +++ b/apps/ctrclk/ChangeLog @@ -1 +1,2 @@ 0.02: Modified for use with new bootloader and firmware +0.03: Changed setWatch to Bangle.setUI diff --git a/apps/ctrclk/app.js b/apps/ctrclk/app.js index 060aac2f9..7f6ab0570 100644 --- a/apps/ctrclk/app.js +++ b/apps/ctrclk/app.js @@ -46,26 +46,25 @@ function drawSegment (align, base, str) { point = base + (maxSegmentWidth / 2) - (g.stringWidth(str) / 2); } - g.setColor(1, 1, 1); + g.setColor(g.theme.fg); g.drawString(str, point, middleY - 4, false); } function drawDots (center) { - g.setColor(0xFD20); + g.setColor("#FA0"); g.fillCircle(center, middleY + 10, 2); g.fillCircle(center, middleY + 40, 2); } function drawLines () { - g.setColor(0.5, 0.5, 0.5); + g.setColor("#777"); g.drawLine(middleX - lineLength, lineY1, middleX + lineLength, lineY1); g.drawLine(middleX - lineLength, lineY2, middleX + lineLength, lineY2); } function drawDate (str) { let maxSegmentWidth = 236; - g.setColor(0.5, 0.5, 0.5); - g.setColor(0.5, 0.5, 0.5); + g.setColor("#777"); g.drawString(str, (maxSegmentWidth) - (g.stringWidth(str)), middleY - 22, false); } @@ -149,6 +148,9 @@ function start () { } start(); +// Show launcher when middle button pressed +Bangle.setUI("clock"); + Bangle.loadWidgets(); Bangle.drawWidgets(); Bangle.on('lcdPower', function (on) { @@ -158,6 +160,3 @@ Bangle.on('lcdPower', function (on) { stop(); } }); - -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); diff --git a/apps/dclock/ChangeLog b/apps/dclock/ChangeLog index edf7da4c2..aa8ae23fe 100644 --- a/apps/dclock/ChangeLog +++ b/apps/dclock/ChangeLog @@ -7,3 +7,4 @@ 0.07: add days in current month (md) and days since new moon (l) 0.08: update icon 0.09: Use localised month and day of the week from locale +0.10: Changed setWatch to Bangle.setUI and allow small screen diff --git a/apps/dclock/clock-dev.js b/apps/dclock/clock-dev.js index d2c08726a..d2c3893d5 100644 --- a/apps/dclock/clock-dev.js +++ b/apps/dclock/clock-dev.js @@ -1,17 +1,18 @@ var locale = require("locale"); /* jshint esversion: 6 */ -const timeFontSize = 4; -const dateFontSize = 3; -const smallFontSize = 2; +const big = g.getWidth()>200; +const timeFontSize = big?4:3; +const dateFontSize = big?3:2; +const smallFontSize = big?2:1; const font = "6x8"; const xyCenter = g.getWidth() / 2; const yposTime = 50; -const yposDate = 85; -const yposTst = 115; -const yposDml = 170; -const yposDayMonth = 195; -const yposGMT = 220; +const yposDate = big?85:75; +const yposTst = big?115:95; +const yposDml = big?170:130; +const yposDayMonth = big?195:140; +const yposGMT = big?220:150; // Check settings for what type our clock should be var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]; @@ -99,6 +100,8 @@ Bangle.on('lcdPower', function(on) { // clean app screen g.clear(); +// Show launcher when button pressed +Bangle.setUI("clock"); Bangle.loadWidgets(); Bangle.drawWidgets(); @@ -107,6 +110,3 @@ setInterval(drawSimpleClock, 100); // draw now drawSimpleClock(); - -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); \ No newline at end of file diff --git a/apps/de-stress/ChangeLog b/apps/de-stress/ChangeLog new file mode 100644 index 000000000..fcd7580b1 --- /dev/null +++ b/apps/de-stress/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Adjust for different screen types and themes diff --git a/apps/de-stress/app.js b/apps/de-stress/app.js index 445e853c5..b292fe601 100644 --- a/apps/de-stress/app.js +++ b/apps/de-stress/app.js @@ -1,29 +1,21 @@ +g.setBgColor(0).clear(); -//g.clear(); - -var img = { - width : 100, height : 100, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("/wA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AglYAGE/44gHz4nnHKtPumez3C4WezFzp49ZLAgoCE4WeugnaHStPG4QAHzw9DE/Y6VHJQ9YMJwnEMk46CF4lzfglPug9WCAWYDQl0E4tzN4gLCMVC5Cp+YGoOezxBCubKHExxUEuiFCEoInECAhkjMQuYLArCFGwLLEHpjsGMIRpEfAsrMkwhBFAQ6BHJA9EKApDBHpAJBQYhPBE5iZBzAsDMb4gBEwg6MJhBkIYorgBPQiMMUAhkeHgkrug7ObI5qBHwhiGZYomOEojGeJQVzTx5kOMQ6JREAR3CDIJkcHobwEMiw+CAAYJEMSYWCAgRjcDgI4CYyiiDXopiFBooAWRwJkaHwjGVKwdzH4rADuhiXZAaICMbbtGHy2YBQ+YRDCiEMbidCULBkDLIwIIdqbmCp5jZPwIfDAYQAXXwNPzwACIQLQIACt0ZDIZBTwRFBHjWeHoSJCETlPc4ZjdAYYA7L4Jj/Mf5jCEYQDDAHhEEMbzH+McBfCMf4ADzxjep+YL/1PMb10MYQDCAHd0MYV0MbdzEYoA7UYdPMTBkCc4hj9leeAYRjbL4aIDAHN0UwRjeL4WYZHlPzBnCMbZkBQojtCAG6gEp5ibZARfCdwjG3ugDBzzGcMYTIEFAQA1ujGFMbjIFeAgA0HobGeZA9PAgYAyG4jGfZAyPBzBizf4LGjMggtCFAJpDAFw0FMURjCeAd0AgYAup90AgZjjMgWYFYZkwGImYMUhkDeYdPuZituZiDzximMYUrFwj6DAFF0TAg6CMcpkCSYpkqMQtPMVBkDuZktMQtzMVRkDLwZkoMQoFBMVZkJp5ijX4JizMhFPMkQjBMWpkHIAKjEADTrGMWZkDuY8DuZkdMQKKEMWpkDUIxFEACocGdoJi1MhCqBAwgATLYLkEMXJkDIY4GEAB+ep9PC4aDBMXRkJukruhiRCgxi+MglzJAtPMR7cGNIJi+MglPJYhSBzBhLzB0FzwWBMX5lGMghVGAAtzld0bwph/MhNzWYzKGNwR2Euhi/MhhTHA4ZrHA4Rh/Mp10YIlzA4JoCBQjE/ZTK8BA45i/MqtzX4jE/Mj0rYQjECBYZP/MrFPMoWep5h/ZT90ujE/MsZh/MsZB/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/ACg")) -}; +var img = require("heatshrink").decompress(atob("plB4X/ln7A4OGmMs5dVAANa1WlAoQADBI9W1QAByoJNtWv4f61ISEtWrBI2q4EAgeqCQgJHq2sLoU6IYdaBIg5CrWAn2q2EDF4dq4EO1W8gQcCtUD1fP9cA9QSC1cA9f/9XADgWohxBBh2whQvBq2gAwMAh+wlQSB1k7IIXogYvBrXAlYJC9k6DYOw9gIChXA9JBC0AJCncOytWwAtBAAMDF4RBDAAMOgWVrXDwDjD9kKy2gIIcAgXD0taDYgvCRIJBDF4OA0trhwIDJgK2B4BKDAAXptcLA4kC4HrD4IJE8HptE4BAhfBLAJBEgEslISGL4JOBBAoSB1ksBIs6/70DBIgSHh+q+AIFnASIABASU0EgCR0IhWgEp4SBHCBxJLzusXowAIaBISLhYSO8EptcOCR2w9NagXACJkDwGlrXDwASMgXDCQOA2ASMh0C0tW2HgCRkLh2Vq2glASMlEKytV1iFN9k6qtVtEOORcD2EpCQNqOwJwL4GpCQNa4GgCRUKgelCQJyBlgSKnBwBCQWgnZdLOAQAB1BfKLoMqCIVVtYHBXZPA9ISDL4PsCRE7LoZfDnRKJLoYAC1kOTI8CDoIREJgMA1gSGFwJKEJgaGH9hKFTIaGGPQKVEAAeogbTFhXASogADtXAlYSE9cDeYRMG2A5EG4MOJQxMC1g5EG4M6JQ4AB1Q5E9ED1QRIHIatBG5Y5EVoM6G5Y5DnXDCwJvIHIsD32AG5Y5C1aUBgHqG5atDLwI3MHIReCG5hgD4aUKHI2+G5xgC1RcNAAdpBJA")) function hr(){ - -Bangle.buzz(100,0.1).then(()=>{ - g.clear(); - return new Promise(resolve=>setTimeout(resolve,250)); // wait 250ms -}).then(()=>{ - return Bangle.buzz(150); -}).then(()=>{ - g.drawImage(img, 25, 40, {scale:2}); -}); - + Bangle.buzz(100,0.1).then(()=>{ + g.clear(); + return new Promise(resolve=>setTimeout(resolve,250)); // wait 250ms + }).then(()=>{ + return Bangle.buzz(150); + }).then(()=>{ + g.drawImage(img, g.getWidth()/2 - 76, g.getHeight()/2 - 65, {scale:2}); + }); } setInterval(hr, 2000); g.flip(); -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); - - +// TODO - not clock but we still want a press to show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/devstopwatch/ChangeLog b/apps/devstopwatch/ChangeLog index c6d24e9bc..e2b392fe9 100644 --- a/apps/devstopwatch/ChangeLog +++ b/apps/devstopwatch/ChangeLog @@ -1,2 +1,3 @@ 0.01: App created 0.02: Persist state to storage to enable stopwatch to continue in the background +0.03: Modified to use setUI, theme and different screens diff --git a/apps/devstopwatch/app.js b/apps/devstopwatch/app.js index 665ba084e..83bb693a9 100644 --- a/apps/devstopwatch/app.js +++ b/apps/devstopwatch/app.js @@ -2,13 +2,16 @@ const EMPTY_LAP = '--:--:---'; const EMPTY_H = '00:00:000'; const MAX_LAPS = 6; const XY_CENTER = g.getWidth() / 2; +const big = g.getWidth()>200; const Y_CHRONO = 40; -const Y_HEADER = 80; -const Y_LAPS = 125; -const Y_BTN3 = 225; +const Y_HEADER = big?80:60; +const Y_LAPS = big?125:90; +const H_LAPS = big?15:8; +const Y_BTN3 = big?225:165; const FONT = '6x8'; const CHRONO = '/* C H R O N O */'; + var reset = false; var currentLap = ''; var chronoInterval; @@ -22,11 +25,11 @@ var state = require("Storage").readJSON("devstopwatch.state.json",1) || { laps: [EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP], }; -// Set laps. -setWatch(() => { - +// Show launcher when button pressed +Bangle.setUI("clockupdown", btn=>{ + if (btn==0) { reset = false; - + if (state.started) { changeLap(); } else { @@ -34,13 +37,9 @@ setWatch(() => { chronoInterval = setInterval(chronometer, 10); } } -}, BTN1, { repeat: true, edge: 'rising' }); - -// Reset chronometre. -setWatch(() => { resetChrono(); }, BTN3, { repeat: true, edge: 'rising' }); - -// Show launcher when middle button pressed. -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: 'falling' }); +} + if (btn==1) resetChrono(); +}); function resetChrono() { state.laps = [EMPTY_H, EMPTY_H, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP, EMPTY_LAP]; @@ -106,33 +105,33 @@ function printChrono() { var print = ''; - g.setFont(FONT, 2); + g.setFont(FONT, big?2:1); print = CHRONO; g.drawString(print, XY_CENTER, Y_CHRONO, true); - g.setColor(0, 220, 0); - g.setFont(FONT, 3); + g.setColor("#0e0"); + g.setFont(FONT, big?3:2); print = ` T ${state.laps[0]}\n`; print += ` C ${state.laps[1]}\n`; g.drawString(print, XY_CENTER, Y_HEADER, true); - g.setColor(255, 255, 255); - g.setFont(FONT, 2); + g.setColor(g.theme.fg); + g.setFont(FONT, big?2:1); for (var i = 2; i < MAX_LAPS + 1; i++) { - g.setColor(255, 255, 255); + g.setColor(g.theme.fg); let suffix = ' '; if (state.currentLapIndex === i) { let suffix = '*'; - g.setColor(255, 200, 0); + g.setColor("#f70"); } const lapLine = `L${i - 1} ${state.laps[i]} ${suffix}\n`; - g.drawString(lapLine, XY_CENTER, Y_LAPS + (15 * (i - 1)), true); + g.drawString(lapLine, XY_CENTER, Y_LAPS + (H_LAPS * (i - 1)), true); } - g.setColor(255, 255, 255); + g.setColor(g.theme.fg); g.setFont(FONT, 1); print = 'Press 3 to reset'; g.drawString(print, XY_CENTER, Y_BTN3, true); @@ -166,7 +165,7 @@ E.on('kill', function(){ }); if(state.started){ - chronoInterval = setInterval(chronometer, 10); + chronoInterval = setInterval(chronometer, 10); } else { - resetChrono(); + resetChrono(); } diff --git a/apps/digiclock/ChangeLog b/apps/digiclock/ChangeLog index 0bb55854e..e41fae573 100644 --- a/apps/digiclock/ChangeLog +++ b/apps/digiclock/ChangeLog @@ -1 +1,2 @@ 0.01: App Made! +0.02: Changed setWatch to Bangle.setUI, code tidy diff --git a/apps/digiclock/digiclock.js b/apps/digiclock/digiclock.js index 7f74f2242..f404d30b2 100644 --- a/apps/digiclock/digiclock.js +++ b/apps/digiclock/digiclock.js @@ -1,141 +1,50 @@ //load fonts require("Font7x11Numeric7Seg").add(Graphics); -require("FontHaxorNarrow7x17").add(Graphics); +require("FontHaxorNarrow7x17").add(Graphics); //screen position -const X = 170; -const Y = 140; +const X = 170; +const Y = 140; function draw() { // Date Variables - var date = new Date(); - var h = date.getHours(); - var m = date.getMinutes(); - var day = date.getDay(); - var month = date.getMonth(); - var dateNum = date.getDate(); - var year = date.getFullYear(); - var half = "AM"; - var time = (" " + h).substr(-2) + ":" + ("0" + m).substr(-2); - - //convert day into string - switch (day) { - case 0: - day = "Sunday"; - break; - - case 1: - day = "Monday"; - break; - - case 2: - day = "Tuesday"; - break; - - case 3: - day = "Wednesday"; - break; - - case 4: - day = "Thursday"; - break; - - case 5: - day = "Friday"; - break; - - case 6: - day = "Saturday"; - break; - - default: - day = "ERROR"; - break; - } - - //convert month into String - switch(month) { - case 0: - month = "Jan"; - break; - - case 1: - month = "Feb"; - break; - - case 2: - month = "Mar"; - break; - - case 3: - month = "Apr"; - break; - - case 4: - month = "May"; - break; - - case 5: - month = "Jun"; - break; - - case 6: - month = "Jul"; - break; - - case 7: - month = "Aug"; - break; - - case 8: - month = "Sep"; - break; - - case 9: - month = "Oct"; - break; - - case 10: - month = "Nov"; - break; - - case 11: - month = "Dec"; - break; - - default: - month = "ERROR"; - break; - - } - + var date = new Date(); + var h = date.getHours(); + var m = date.getMinutes(); + var day = require("locale").dow(date); + var month = require("locale").month(date,1); + var dateNum = date.getDate(); + var year = date.getFullYear(); + var half = "AM"; + var time = (" " + h).substr(-2) + ":" + ("0" + m).substr(-2); + if (h > 12) { - half = "PM"; - h = h - 12; + half = "PM"; + h = h - 12; } //reset graphics - g.reset(); + g.reset(); //draw the time g.setFont("7x11Numeric7Seg", 5); g.setFontAlign(1,1); g.drawString(time, X, Y, true /*clear background*/); - g.setFont("7x11Numeric7Seg", 3); - g.drawString(("0"+date.getSeconds()).substr(-2), X+50, Y, true /*clear background*/); + g.setFont("7x11Numeric7Seg", 3); + g.drawString(("0"+date.getSeconds()).substr(-2), X+50, Y, true /*clear background*/); g.setFontAlign(0,1); g.setFont("HaxorNarrow7x17", 2); g.drawString(half, X+30, Y-35, true); g.setFont("HaxorNarrow7x17", 3); g.drawString(day, X-60, Y+53, true); - g.drawString(month, X-100, Y+95, true); - g.drawString(dateNum, X-40, Y+95, true); - g.drawString(year, X-90, Y-55, true); - - + g.drawString(month, X-100, Y+95, true); + g.drawString(dateNum, X-40, Y+95, true); + g.drawString(year, X-90, Y-55, true); + + } //clear screen at startup -g.clear(); +g.clear(); //draw immediatly -draw(); +draw(); var secondInterval = setInterval(draw, 1000); // Stop updates when LCD is off, restart when on @@ -148,7 +57,7 @@ Bangle.on('lcdPower',on=>{ } }); +// Show launcher when button pressed +Bangle.setUI("clock"); Bangle.loadWidgets(); Bangle.drawWidgets(); - -setWatch(Bangle.showLauncher, BTN2, {repeat : false, edge: "falling"}); diff --git a/apps/dotclock/ChangeLog b/apps/dotclock/ChangeLog index c9658afb8..563db87e7 100644 --- a/apps/dotclock/ChangeLog +++ b/apps/dotclock/ChangeLog @@ -1,2 +1,3 @@ 0.01: Based on the Analog Clock app, minimal dot 0.02: Remove hardcoded hour buzz (you can install widchime if you miss it) +0.03: Use setUI, adjust for themes and different size screens diff --git a/apps/dotclock/clock-dot.js b/apps/dotclock/clock-dot.js index c4a8be921..66255d1b4 100644 --- a/apps/dotclock/clock-dot.js +++ b/apps/dotclock/clock-dot.js @@ -1,20 +1,22 @@ -let g; -let Bangle; - +const big = g.getWidth()>200; const locale = require('locale'); const p = Math.PI / 2; const pRad = Math.PI / 180; -const faceWidth = 100; // watch face radius let timer = null; let currentDate = new Date(); -let hourRadius = 60; -let minRadius = 80; -const centerPx = g.getWidth() / 2; +const faceWidth = big?100:65; // watch face radius +let hourRadius = big?60:40; +let minRadius = big?80:55; +const centerX = g.getWidth() / 2; +const centerY = 24 + (g.getHeight()-24) / 2; +let colSecA = g.theme.dark ? "#00A" : "#58F"; // before the second +let colSecB = g.theme.dark ? "#58F" : "#00A"; // after the second +let colSec1 = g.theme.dark ? "#F83" : "#000"; // ON the second const seconds = (angle) => { const a = angle * pRad; - const x = centerPx + Math.sin(a) * faceWidth; - const y = centerPx - Math.cos(a) * faceWidth; + const x = centerX + Math.sin(a) * faceWidth; + const y = centerY - Math.cos(a) * faceWidth; // if 15 degrees, make hour marker larger const radius = (angle % 15) ? 2 : 4; @@ -23,15 +25,15 @@ const seconds = (angle) => { const hourDot = (angle,radius) => { const a = angle * pRad; - const x = centerPx + Math.sin(a) * hourRadius; - const y = centerPx - Math.cos(a) * hourRadius; + const x = centerX + Math.sin(a) * hourRadius; + const y = centerY - Math.cos(a) * hourRadius; g.fillCircle(x, y, radius); }; const minDot = (angle,radius) => { const a = angle * pRad; - const x = centerPx + Math.sin(a) * minRadius; - const y = centerPx - Math.cos(a) * minRadius; + const x = centerX + Math.sin(a) * minRadius; + const y = centerY - Math.cos(a) * minRadius; g.fillCircle(x, y, radius); }; @@ -45,54 +47,49 @@ const drawAll = () => { // draw all secs for (let i = 0; i < 60; i++) { - if (i > currentSec) { - g.setColor(0, 0, 0.6); - } else { - g.setColor(0.3, 0.3, 1); - } + g.setColor((i > currentSec) ? colSecA : colSecB); seconds((360 * i) / 60); } onSecond(); }; const resetSeconds = () => { - g.setColor(0, 0, 0.6); + g.setColor(colSecA); for (let i = 0; i < 60; i++) { seconds((360 * i) / 60); } }; const drawMin = () => { - g.setColor(0.5, 0.5, 0.5); + g.setColor("#777"); for (let i = 0; i < 60; i++) { minDot((360 * i) / 60,1); } }; const drawHour = () => { - g.setColor(0.5, 0.5, 0.5); + g.setColor("#777"); for (let i = 0; i < 12; i++) { hourDot((360 * 5 * i) / 60,1); } }; const onSecond = () => { - g.setColor(0.3, 0.3, 1); + g.setColor(colSecB); seconds((360 * currentDate.getSeconds()) / 60); if (currentDate.getSeconds() === 59) { resetSeconds(); onMinute(); } - g.setColor(1, 0.7, 0.2); + g.setColor(colSec1); currentDate = new Date(); seconds((360 * currentDate.getSeconds()) / 60); - g.setColor(1, 1, 1); + g.setColor(g.theme.fg); }; const drawDate = () => { g.reset(); - g.setColor(1, 1, 1); - g.setFont('6x8', 2); + g.setFont('6x8', big?2:1); const dayString = locale.dow(currentDate, true); // pad left date @@ -101,7 +98,7 @@ const drawDate = () => { // console.log(`${dayString}|${dateString}`); // center date const l = (g.getWidth() - g.stringWidth(dateDisplay)) / 2; - const t = centerPx - 6 ; + const t = centerY - 6 ; g.drawString(dateDisplay, l, t); // console.log(l, t); }; @@ -111,7 +108,7 @@ const onMinute = () => { resetSeconds(); } // clear existing hands - g.setColor(0, 0, 0); + g.setColor(g.theme.bg); hourDot((360 * currentDate.getHours()) / 12,4); minDot((360 * currentDate.getMinutes()) / 60,3); @@ -125,7 +122,7 @@ const onMinute = () => { g.setColor(1, 0, 0); // Hour hourDot((360 * currentDate.getHours()) / 12,4); - g.setColor(1, 0.9, 0.9); + g.setColor(g.theme.fg2); // Minute minDot((360 * currentDate.getMinutes()) / 60,3); drawDate(); @@ -152,8 +149,8 @@ g.clear(); resetSeconds(); startTimers(); drawAll(); +// Show launcher when button pressed +Bangle.setUI("clock"); + Bangle.loadWidgets(); Bangle.drawWidgets(); - -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); diff --git a/apps/dotmatrixclock/app.js b/apps/dotmatrixclock/app.js old mode 100755 new mode 100644 diff --git a/apps/dotmatrixclock/dotmatrix-clock-screen-shot.png b/apps/dotmatrixclock/dotmatrix-clock-screen-shot.png old mode 100755 new mode 100644 diff --git a/apps/dotmatrixclock/dotmatrixclock.png b/apps/dotmatrixclock/dotmatrixclock.png old mode 100755 new mode 100644 diff --git a/apps/gallifr/ChangeLog b/apps/gallifr/ChangeLog index c785cbd67..0e1f45042 100644 --- a/apps/gallifr/ChangeLog +++ b/apps/gallifr/ChangeLog @@ -1 +1,2 @@ 0.01: First released version +0.02: Changed setWatch to Bangle.setUI diff --git a/apps/gallifr/app.js b/apps/gallifr/app.js index 281988ad7..d327bcdc1 100644 --- a/apps/gallifr/app.js +++ b/apps/gallifr/app.js @@ -243,5 +243,5 @@ startTimers(); Bangle.loadWidgets(); drawAll(); -// Show launcher when middle button pressed -setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index b4ce71907..70abfd610 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -1,4 +1,7 @@ (() => { + // Current shown notification, saved for dismissing. + var currentNot = null; + // Music handling const state = { music: "stop", @@ -151,16 +154,19 @@ global.GB = (event) => { switch (event.t) { case "notify": - case "notify-": - if (event.t === "notify") { - require("notify").show(prettifyNotificationEvent(event)); - if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) { - Bangle.buzz(); - } - } else { // notify- - require("notify").hide(event); + currentNot = prettifyNotificationEvent(event); + require("notify").show(currentNot); + if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) { + Bangle.buzz(); } break; + case "notify-": + currentNot.t = "notify"; + currentNot.n = "DISMISS"; + gbSend(currentNot); + currentNot = null; + require("notify").hide(event); + break; case "musicinfo": state.musicInfo = event; updateMusic({on: false}); diff --git a/apps/gbtwist/ChangeLog b/apps/gbtwist/ChangeLog new file mode 100644 index 000000000..ec66c5568 --- /dev/null +++ b/apps/gbtwist/ChangeLog @@ -0,0 +1 @@ +0.01: Initial version diff --git a/apps/gbtwist/README.md b/apps/gbtwist/README.md new file mode 100644 index 000000000..7e9dbcbe5 --- /dev/null +++ b/apps/gbtwist/README.md @@ -0,0 +1,15 @@ +# Gadgetbridge Twist Control + +Control your music app (e.g. MortPlayer Music [a folder based, not tag based player] ) that handles multiple play-commands (same as using a single-button-headset's button to change songs) on your Gadgetbridge-connected phone. +- Activate counting for 4 seconds with a twist (beeps at start and end of counting) +- twist multiple times for: + play/pause (1), + next song (2), + prev. song (3), + next folder (4), + prev. folder (5), + reset counter (6) +- the command to be sent is shown in green +- Volume up/down is controlled by BTN1/BTN3 presses + +![screenshot1](https://user-images.githubusercontent.com/84921310/119907374-65bb6180-bf50-11eb-9073-f29f7e333e00.jpg) diff --git a/apps/gbtwist/app-icon.js b/apps/gbtwist/app-icon.js new file mode 100644 index 000000000..b28bbe664 --- /dev/null +++ b/apps/gbtwist/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwIYVhAFEjgFEh4FEg+AAocD4AME8ADCgPAvAFCj/8nkQAoN//8enAQB///44FBgYFB8f4FoIFB+IFBh/+n/4AocH/AXBj/+gP8FIIFDFwM//0x/wFDAIIFNv4FB/4FNEaIFFj/gn5HCj+AAoUEh4FBMgUP4AFDw/gv/wAoPDPoKhBjnxAoKtBjl4TYLICninBagUPWYLJPFoIADZIYABnj6KABIA=")) diff --git a/apps/gbtwist/app.js b/apps/gbtwist/app.js new file mode 100644 index 000000000..4bd495277 --- /dev/null +++ b/apps/gbtwist/app.js @@ -0,0 +1,97 @@ +// just a watch, to fill an empty screen + +function drwClock() { + var d = new Date(); + var h = d.getHours(), m = d.getMinutes(); + var time = ("0"+h).substr(-2) + ":" + ("0"+m).substr(-2); + g.reset(); + g.setFont('6x8',7); + g.setFontAlign(-1,-1); + g.drawString(time,20,80); +} + +g.clear(); +drwClock(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +///////////////////////////////////////////////////////////// +// control music by twist/buttons + +var counter = 0; //stores your counted your twists +var tstate = false; //are you ready to count the twists? + +function playx() { + Bluetooth.println(JSON.stringify({t:"music", n:"play"})); +} + +function volup() { + Bluetooth.println(JSON.stringify({t:"music", n:"volumeup"})); +} + +function voldn() { + Bluetooth.println(JSON.stringify({t:"music", n:"volumedown"})); +} + +function sendCmd() { + print (counter); + Bangle.beep(200,3000); + if (tstate==false && counter>0){ + do {playx(); counter--;} + while (counter >= 1); + } +} + +function twistctrl() { + if (tstate==false){ + tstate=true; + setTimeout('tstate=false',4000); + setTimeout(sendCmd,4100); + Bangle.beep(200,3000); + } + else{ + g.clearRect(10,140,230,200); + if (tstate==true){ + if (counter < 5){ + counter++; + drwCmd(); + Bangle.buzz(100,2); + } + else { + counter = 0; + Bangle.buzz(400); + } + } + } +} + +function drwCmd() { + g.setFont('6x8',6); + g.setColor(0.3,1,0.3); + g.clearRect(10,140,230,200); +switch (counter){ + case 1: + g.drawString('play',50,150); + break; + case 2: + g.drawString('next',50,150); + break; + case 3: + g.drawString('prev',50,150); + break; + case 4: + g.drawString('nx f',50,150); + break; + case 5: + g.drawString('pr f',50,150); + break; + case 0: + g.clearRect(10,140,230,200); + break; +} +} + +setWatch(volup,BTN1,{repeat:true}); +setWatch(voldn,BTN3,{repeat:true}); +Bangle.on('twist',twistctrl); +setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); \ No newline at end of file diff --git a/apps/gbtwist/app.png b/apps/gbtwist/app.png new file mode 100644 index 000000000..2379c76f0 Binary files /dev/null and b/apps/gbtwist/app.png differ diff --git a/apps/geissclk/ChangeLog b/apps/geissclk/ChangeLog index bd718a5b1..7458fadee 100644 --- a/apps/geissclk/ChangeLog +++ b/apps/geissclk/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: BTN2->launcher, use smaller text to allow "20:00" to fit on screen +0.03: Changed setWatch to Bangle.setUI diff --git a/apps/geissclk/clock.js b/apps/geissclk/clock.js index 7d63b815e..f14ea5f39 100644 --- a/apps/geissclk/clock.js +++ b/apps/geissclk/clock.js @@ -148,4 +148,5 @@ Bangle.drawWidgets(); iterate(); animInterval = setInterval(iterate, 50); -setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); +// Show launcher when button pressed +Bangle.setUI("clock"); diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog index 412dbe9d3..289fb38d6 100644 --- a/apps/gpsrec/ChangeLog +++ b/apps/gpsrec/ChangeLog @@ -21,3 +21,6 @@ 0.17: Disable recording if storage is full (fix #574) 0.18: Period counter now uses GPS time rather than counting packets (allows use with GPS Setup) 0.19: Fix memory usage issues inside track viewer app +0.20: Add documentation to explain time needed for getting a time fix +0.21: Fix issue where a period of 1s recorded every 2s, 5s every 6s, and so on +0.22: Ensure Bangle.setGPSPower uses 'gpsrec' as a tag diff --git a/apps/gpsrec/README.md b/apps/gpsrec/README.md new file mode 100644 index 000000000..72f744452 --- /dev/null +++ b/apps/gpsrec/README.md @@ -0,0 +1,10 @@ +# GPS Recorder + +![icon](app.png) + +This app allows you to record a GPS track. It can run in background. The data can later be exported as KML or GPX files via the BangleJS app store. + +## Tips + +When you turn on recording, a widget badge that looks like a satellite will appear immediately at the top of the screen. However, the recording does not begin immediately. It usually takes several minutes for the watch to get a [GPS fix](https://en.wikipedia.org/wiki/Time_to_first_fix). You will notice a blinking question mark at the lower left of the badge indicating currently getting a fix. The badge will change when a GPS fix is achieved and that is when the app actually starts writing data to the log file. You can [upload assistant files](https://banglejs.com/apps/#assisted%20gps%20update) to speed up the time spent on getting a GPS fix. + diff --git a/apps/gpsrec/interface.html b/apps/gpsrec/interface.html index 11b53164f..4c7270f0a 100644 --- a/apps/gpsrec/interface.html +++ b/apps/gpsrec/interface.html @@ -95,6 +95,7 @@ function getTrackList() { Util.showModal("Loading Tracks..."); domTracks.innerHTML = ""; Puck.write(`\x10(function() { + Bluetooth.println(""); for (var n=0;n<36;n++) { var f = require("Storage").open(".gpsrc"+n.toString(36),"r"); var l = f.readLine(); diff --git a/apps/gpsrec/widget.js b/apps/gpsrec/widget.js index 8e4286db5..3d22373ec 100644 --- a/apps/gpsrec/widget.js +++ b/apps/gpsrec/widget.js @@ -29,7 +29,7 @@ var period = 1000000; if (lastFixTime!==undefined) period = fix.time.getTime() - lastFixTime; - if (period > settings.period*1000) { + if (period+500 > settings.period*1000) { // round up lastFixTime = fix.time.getTime(); try { if (gpsTrack) gpsTrack.write([ @@ -69,7 +69,7 @@ gpsTrack = undefined; } if (gOn != gpsOn) { - Bangle.setGPSPower(gOn); + Bangle.setGPSPower(gOn,"gpsrec"); gpsOn = gOn; } } diff --git a/apps/gpsservice/README.md b/apps/gpsservice/README.md index b1e3e60d4..061f3ba3b 100644 --- a/apps/gpsservice/README.md +++ b/apps/gpsservice/README.md @@ -2,6 +2,9 @@ A configurable, low power GPS widget that runs in the background. +NOTE: This app has been superceded by [gpssetup](https://github.com/espruino/BangleApps/blob/master/apps/gpssetup/README.md) + + ## Goals To develop a low power GPS widget that runs in the background and to diff --git a/apps/hrm/ChangeLog b/apps/hrm/ChangeLog index 1efe78c07..6cedf8f1b 100644 --- a/apps/hrm/ChangeLog +++ b/apps/hrm/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Use HRM data and calculations from Bangle.js (don't access hardware directly) 0.03: Fix timing issues, and use 1/2 scale to keep graph on screen +0.04: Update for new firmwares that have a 'HRM-raw' event diff --git a/apps/hrm/heartrate.js b/apps/hrm/heartrate.js index 1ec0a31d2..09e8a826e 100644 --- a/apps/hrm/heartrate.js +++ b/apps/hrm/heartrate.js @@ -4,18 +4,25 @@ Bangle.setHRMPower(1); var hrmInfo, hrmOffset = 0; var hrmInterval; function onHRM(h) { - // this is the first time we're called if (counter!==undefined) { + // the first time we're called remove + // the countdown counter = undefined; g.clear(); } hrmInfo = h; - hrmOffset = 0; + /* On 2v09 and earlier firmwares the only solution for realtime + HRM was to look at the 'raw' array that got reported. If you timed + it right you could grab the data pretty much as soon as it was written. + In new firmwares, '.raw' is not available. */ if (hrmInterval) clearInterval(hrmInterval); hrmInterval = undefined; - setTimeout(function() { - hrmInterval = setInterval(readHRM,41); - }, 40); + if (hrmInfo.raw) { + hrmOffset = 0; + setTimeout(function() { + hrmInterval = setInterval(readHRM,41); + }, 40); + } var px = g.getWidth()/2; g.setFontAlign(0,0); @@ -28,13 +35,32 @@ function onHRM(h) { g.drawString("BPM",px+15,45); } Bangle.on('HRM', onHRM); +/* On newer (2v10) firmwares we can subscribe to get +HRM events as they happen */ +Bangle.on('HRM-raw', function(v) { + var a = v.raw; + hrmOffset++; + if (hrmOffset>g.getWidth()) { + hrmOffset=0; + g.clearRect(0,90,239,239); + g.moveTo(-100,0); + } + + y = E.clip(170 - (v.raw*2),100,230); + g.setColor(1,1,1); + g.lineTo(hrmOffset, y); +}); // It takes 5 secs for us to get the first HRM event var counter = 5; function countDown() { - E.showMessage("Please wait...\n"+counter--); - if (counter) setTimeout(countDown, 1000); + if (counter) { + g.drawString(counter--,g.getWidth()/2,g.getHeight()/2, true); + setTimeout(countDown, 1000); + } } +g.clear().setFont("6x8",2).setFontAlign(0,0); +g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16); countDown(); diff --git a/apps/kitchen/ChangeLog b/apps/kitchen/ChangeLog index 71548ec30..1df5d867f 100644 --- a/apps/kitchen/ChangeLog +++ b/apps/kitchen/ChangeLog @@ -8,3 +8,5 @@ 0.08: Improved error handling for missing firmware features, added template app.kit.js 0.09: Added heart rate monitor app 0.10: Converted Stepo to use direct screen writes, added a Trip Counter feature to stepo +0.11: Detect when waypoints.json is not present, error E-WPT +0.12: Added stepo2 as a replacement for stepo and digi diff --git a/apps/kitchen/README.md b/apps/kitchen/README.md index a829a39b0..2a1b148fd 100644 --- a/apps/kitchen/README.md +++ b/apps/kitchen/README.md @@ -41,9 +41,25 @@ The following buttons depend on which face is currently in use - Waypointer : select next waypoint +## Stepo2 +![](screenshot_stepo2.jpg) + +- Requires one of the pedominter widgets to be installed +- Stepo2 is a combination of Stepo and Digi and now replaces them +- Displays the time in large font +- Display current step count in a doughnut gauge +- The gauge show percentage of steps out of a goal of 10000 steps +- When the battery is less than 25% the doughnut turns red +- Use BTN1 to switch to the Trip Counter, use long press to reset Trip Counter +- Use BTN1 to cycle through the displays of Day,Date, Trip Counter, Battery %, Mem % and Firmware +- Use BTN3 to switch to the next app + + + ## Stepo ![](screenshot_stepo.jpg) +- now replaced by Stepo2 but still available if you install manually - Requires one of the pedominter widgets to be installed - Displays the time in large font - Display current step count in a doughnut gauge @@ -62,11 +78,13 @@ The following buttons depend on which face is currently in use ## Digi ![](screenshot_digi.jpg) +- now replaced by Stepo2 but still available if you install manually - Displays the time in large font - Display day and date - Use BTN1 to switch between display of battery and memory %. - Use BTN3 to switch to the next app. + ## Swatch ![](screenshot_swatch.jpg) - A simple stopwatch @@ -76,6 +94,9 @@ The following buttons depend on which face is currently in use ## Heart ![](screenshot_heart.jpg) - A simple heart rate monitor, at present the app is just showing the raw value from HRM.bpm +- This is an experimental app and not installed by default. The + heart.kit.js file can be uploaded via the Espruino IDE if you want + to try it out. Then reload the App. - BTN1, long press, turn heart rate monitor on / off ## Waypointer @@ -226,12 +247,12 @@ I have settled on directly writing to the screen using the Graphics object (g.) for the compass App. This creates a bit of flicker when the arrow moves but is more reliable than using the ArrayBuffer. -v0.09: Since adding the heart rate monitor I have noticed that I can -sometimes can a memory error when switch through the Apps back to the -Stepo App. I think this can be cured by statically allocating the -ArrayBuffer for stepo rather than using new everytime you switch back -into the stepo watch face. The problem is that the bangle memory -management / defragmentation is quite slow to run. +v0.09: Since adding the heart rate monitor I have sometimes observed +a low memory error when switching through the Apps back to the Stepo +App. I think this can be cured by statically allocating the +ArrayBuffer for stepo rather than using 'new' everytime you switch +back into the stepo watch face. The problem is that the bangle +memory management / defragmentation is quite slow to run. v0.10: Revisited having a display buffer for the stepo part of the App. Now use direct screen writing as it means less memory allocation and @@ -241,16 +262,21 @@ reduces chance of getting a memory error on switching watch faces. The following error codes will be displayed if one of the dependancies is not met. -* E-STEPS - no pedomintor widget has been installed, please install the widpedom or the activepedom widgets -* E-CALIB - no compass calibration data was found, see 'Compass Calibration' -* E-FW - require firmware 2v08.187 or later to detect gps and compass power status +* E-STEPS - no pedomintor widget has been installed, please install + the widpedom or the activepedom widgets +* E-CALIB - no compass calibration data was found, see 'Compass + Calibration' +* E-FW - require firmware 2v08.187 or later to detect gps and compass + power status +* E-WPT - missing waypoints.json file ### Issues / Future enhancements +* Add a settings app so that 'Kitchen' based clocks can be enabled/disabled * GPS time display shows GMT and not BST, needs localising -* Occassional buzzing after 2-3 days of use, seems to disappear after - a reset to the launcher menu. Needs investigation * Automatically switch the GPS power setting from Super-E to PSMOO 10 seconds after the LCD goes off. At present I just rely on using the GPSSetup app and set the GPS power mode that I want. * Add a small graph to the heart rate monitor app +* Add a facility to call the Arrow calibration process +* Maybe create waypoints.json file if missing diff --git a/apps/kitchen/annex.js b/apps/kitchen/annex.js new file mode 100644 index 000000000..d789f5d0c --- /dev/null +++ b/apps/kitchen/annex.js @@ -0,0 +1,29 @@ +// annexed code that might be worth keeping + +/***************************************************************************** + +Screen Buffer Object that can be shared between faces + +Making into a Class like this means we allocate the memory once +and avoid fragmenting the memory when we switch in and out of faces + +******************************************************************************/ + +function BUF() { + this.pal4color = new Uint16Array([0x0000,0xFFFF,0x7BEF,0xAFE5],0,2); // b,w,grey,greenyellow + this.pal4red = new Uint16Array([0x0000,0xFFFF,0xF800,0xAFE5],0,2); // b,w,red,greenyellow + this.buf = Graphics.createArrayBuffer(120,120,2,{msb:true}); +} + +BUF.prototype.flip = function(x,y) { + g.drawImage({width:120,height:120,bpp:2, buffer:this.buf.buffer, palette:this.pal4color}, x, y); + this.buf.clear(); +} + +BUF.prototype.flip_red = function(x,y) { + g.drawImage({width:120,height:120,bpp:2, buffer:this.buf.buffer, palette:this.pal4red}, x, y); + this.buf.clear(); +} + +let bufObj = new BUF(); + diff --git a/apps/kitchen/compass.kit.js b/apps/kitchen/compass.kit.js index 530ba021c..b20cdce2c 100644 --- a/apps/kitchen/compass.kit.js +++ b/apps/kitchen/compass.kit.js @@ -17,13 +17,12 @@ } function init(gps,sw, hrm) { - showMem("compass init() START"); gpsObject = gps; intervalRefSec = undefined; bearing = 0; // always point north if GPS is off heading = 0; oldHeading = 0; - previous = {hding:"-", bs:"-", dst:"-", wp_name:"-", course:999}; + resetPrevious(); loc = require("locale"); CALIBDATA = require("Storage").readJSON("magnav.json",1)||null; getWaypoint(); @@ -34,12 +33,9 @@ */ if (!Bangle.isCompassOn()) Bangle.setCompassPower(1); gps.determineGPSState(); - - showMem("compass init() END"); } function freeResources() { - showMem("compass freeResources() START"); gpsObject = undefined; intervalRefSec = undefined; previous = undefined; @@ -50,7 +46,6 @@ CALIBDATA = undefined; wp = undefined; if (Bangle.isCompassOn !== undefined && Bangle.isCompassOn()) Bangle.setCompassPower(0); - showMem("compass freeResources() END"); } function startTimer() { @@ -67,12 +62,6 @@ if (Bangle.isCompassOn !== undefined && Bangle.isCompassOn()) Bangle.setCompassPower(0); } - function showMem(msg) { - var val = process.memory(); - var str = msg + " " + Math.round(val.usage*100/val.total) + "%"; - log_debug(str); - } - function onButtonShort(btn) { log_debug("onButtonShort()"); if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return; @@ -206,12 +195,12 @@ drawCompass(dir, 0xFFC0); // yellow oldHeading = dir; } - + if (gpsObject.getState() === gpsObject.GPS_RUNNING) { drawGPSData(); } else { drawCompassHeading(); - } + } } // only used when acting as compass with GPS off diff --git a/apps/kitchen/kitchen.app.js b/apps/kitchen/kitchen.app.js index c3f7bd74d..a7196ba83 100644 --- a/apps/kitchen/kitchen.app.js +++ b/apps/kitchen/kitchen.app.js @@ -2,7 +2,7 @@ var FACES = []; var STOR = require("Storage"); STOR.list(/\.kit\.js$/).forEach(face=>FACES.push(eval(require("Storage").read(face)))); -var iface = STOR.list(/\.kit\.js$/).indexOf("stepo.kit.js"); +var iface = STOR.list(/\.kit\.js$/).indexOf("stepo2.kit.js"); var face = FACES[iface](); var firstPress var pressTimer; @@ -33,10 +33,10 @@ function nextFace(){ // when you feel the buzzer you know you have done a long press function longPressCheck() { Bangle.buzz(); - debug_log("long PressCheck() buzz"); + debug_log("BUZZ, long press"); if (pressTimer) { clearInterval(pressTimer); - debug_log("clear pressTimer 2"); + debug_log("CLEAR pressTimer 2"); pressTimer = undefined; } } @@ -48,10 +48,10 @@ function buttonPressed(btn) { } else { firstPress = getTime(); if (pressTimer) { - debug_log("clear pressTimer 1"); + debug_log("CLEAR pressTimer 1"); clearInterval(pressTimer); } - debug_log("set pressTimer 1"); + debug_log("SET pressTimer 1"); pressTimer = setInterval(longPressCheck, 1500); } } @@ -60,7 +60,7 @@ function buttonPressed(btn) { function buttonReleased(btn) { var dur = getTime() - firstPress; if (pressTimer) { - debug_log("clear pressTimer 3"); + debug_log("CLEAR pressTimer 3"); clearInterval(pressTimer); pressTimer = undefined; } @@ -256,7 +256,7 @@ GPS.prototype.processFix = function(fix) { this.gpsState = this.GPS_RUNNING; if (!this.last_fix.fix && !(require("Storage").readJSON("setting.json", 1) || {}).quiet) { Bangle.buzz(); // buzz on first position - debug_log("GPS fix buzz"); + debug_log("BUZZ - gps fix"); } this.last_fix = fix; } @@ -303,7 +303,7 @@ GPS.prototype.getWPdistance = function() { //log_debug(this.last_fix); //log_debug(this.wp_current); - if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0) + if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0) return 0; else return this.calcDistance(this.last_fix, this.wp_current); @@ -313,14 +313,14 @@ GPS.prototype.getWPbearing = function() { //log_debug(this.last_fix); //log_debug(this.wp_current); - if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0) + if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0) return 0; else return this.calcBearing(this.last_fix, this.wp_current); } GPS.prototype.loadFirstWaypoint = function() { - var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}]; + var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}]; this.wp_index = 0; this.wp_current = waypoints[this.wp_index]; log_debug(this.wp_current); @@ -332,7 +332,7 @@ GPS.prototype.getCurrentWaypoint = function() { } GPS.prototype.waypointHasLocation = function() { - if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0) + if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0) return false; else return true; @@ -340,12 +340,12 @@ GPS.prototype.waypointHasLocation = function() { GPS.prototype.markWaypoint = function() { - if(this.wp_current.name === "NONE") + if(this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE") return; log_debug("GPS::markWaypoint()"); - var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}]; + var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}]; this.wp_current = waypoints[this.wp_index]; if (this.waypointHasLocation()) { @@ -360,7 +360,7 @@ GPS.prototype.markWaypoint = function() { } GPS.prototype.nextWaypoint = function(inc) { - var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}]; + var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}]; this.wp_index+=inc; if (this.wp_index>=waypoints.length) this.wp_index=0; if (this.wp_index<0) this.wp_index = waypoints.length-1; @@ -731,14 +731,14 @@ function TRIP() { TRIP.prototype.resetTrip = function(steps) { this.tripStart = (0 + steps); - console.log("resetTrip starting=" + this.tripStart); + log_debug("resetTrip starting=" + this.tripStart); } TRIP.prototype.getTrip = function(steps) { let tripSteps = (0 + steps) - this.tripStart; - console.log("getTrip steps=" + steps); - console.log("getTrip tripStart=" + this.tripStart); - console.log("getTrip=" + tripSteps); + log_debug("getTrip steps=" + steps); + log_debug("getTrip tripStart=" + this.tripStart); + log_debug("getTrip=" + tripSteps); return tripSteps; } @@ -758,7 +758,6 @@ Debug Object ******************************************************************************/ -/* function DEBUG() { this.logfile = require("Storage").open("debug.log","a"); } @@ -770,7 +769,6 @@ DEBUG.prototype.log = function(msg) { } debugObj = new DEBUG(); -*/ function debug_log(m) { //debugObj.log(m); diff --git a/apps/kitchen/screenshot_stepo2.jpg b/apps/kitchen/screenshot_stepo2.jpg new file mode 100644 index 000000000..acff792b0 Binary files /dev/null and b/apps/kitchen/screenshot_stepo2.jpg differ diff --git a/apps/kitchen/stepo2.kit.js b/apps/kitchen/stepo2.kit.js new file mode 100644 index 000000000..1133314dd --- /dev/null +++ b/apps/kitchen/stepo2.kit.js @@ -0,0 +1,256 @@ +(() => { + function getFace(){ + var intervalRefSec; + var trip; + var prevSteps; + var prevTopText1; + var prevTopText2; + var prevBottomText; + var prevMins; + var infoMode; + + const INFO_DATE = 0; + const INFO_TRIP = 1; + const INFO_BATT = 2; + const INFO_MEM = 3; + const INFO_FW = 4; + + function init(g,sw,hrm,tr) { + trip = tr; + infoMode = INFO_DATE; + forceRedraw(); + } + + function freeResources() { + trip = undefined; + } + + function forceRedraw() { + prevStepsText = ''; + prevSteps = -1; + prevTopText1 = ''; + prevTopText2 = ''; + prevBottomText = ''; + prevMins = ''; + } + + function cycleInfoMode() { + switch(infoMode) { + case INFO_DATE: + infoMode = INFO_TRIP; + break; + case INFO_TRIP: + infoMode = INFO_BATT; + break; + case INFO_BATT: + infoMode = INFO_MEM + break; + case INFO_MEM: + infoMode = INFO_FW + break; + case INFO_FW: + default: + infoMode = INFO_DATE; + break; + } + } + + function onButtonShort(btn) { + cycleInfoMode(); + forceRedraw(); + draw(); + } + + function onButtonLong(btn) { + trip.resetTrip(getSteps()); + infoMode = INFO_TRIP; + forceRedraw(); + draw(); + } + + function radians(a) { + return a*Math.PI/180; + } + + function startTimer() { + draw(); + intervalRefSec = setInterval(draw, 5000); + } + + function stopTimer() { + if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);} + } + + function draw() { + var d = new Date(); + var da = d.toString().split(" "); + var hh = da[4].substr(0,2); + var mm = da[4].substr(3,2); + var day = da[0]; + var day_month = da[2] + " " + da[1]; + + g.setColor(1,1,1); // white + + if (prevMins != mm) { + prevMins = mm; + // hours and minutes + g.clearRect(0, 24, 149, 239); + g.setFontAlign(-1, -1); + g.setFont("Vector", 104); + g.drawString(hh, 20, 30, true); + g.drawString(mm, 20, 120, true); + } + + /* + * if our trip count is greater than todays steps then we have + * rolled over to the next day so we should reset the trip counter + */ + var steps = getSteps(); + if (trip.getTrip(steps) < 0) + trip.resetTrip(steps); + + drawTopText(day,day_month); + drawBottomText(); + drawSteps(); + } + + function drawTopText(dy, dm) { + var topText1 = ""; + var topText2 = ""; + + switch(infoMode) { + case INFO_DATE: + topText1 = dy.toUpperCase(); + topText2 = dm.toUpperCase(); + break; + case INFO_TRIP: + topText2 = "TRIP"; + break; + case INFO_BATT: + topText2 = "BATT"; + break; + case INFO_MEM: + topText2 = "MEM"; + break; + case INFO_FW: + topText2 = "F/W"; + break; + } + + if (prevTopText1 !== topText1 || prevTopText2 !== topText2) { + prevTopText1 = topText1; + prevTopText2 = topText2; + + // day, date + g.setFont("Vector", 24); + g.setFontAlign(0, -1); + g.clearRect(150, 30, 239, 75); + g.drawString(topText1, 195, 30, true); + g.drawString(topText2, 195, 55, true); + } + } + + function drawBottomText() { + var bottomText = ""; + var steps = getSteps(); + + switch(infoMode) { + case INFO_DATE: + bottomText = "" + steps; + break; + case INFO_TRIP: + bottomText = "" + trip.getTrip(steps); + break; + case INFO_BATT: + bottomText = "" + E.getBattery() + "%"; + break; + case INFO_MEM: + var val = process.memory(); + bottomText = "" + Math.round(val.usage*100/val.total) + "%"; + break; + case INFO_FW: + bottomText = process.env.VERSION; + break; + } + + if (prevBottomText !== bottomText) { + prevBottomText = bottomText; + g.clearRect(148, 190, 239, 239); + g.setColor(1,1,1); // white + g.setFont("Vector", 24); + g.setFontAlign(0, -1); + g.drawString(bottomText, 195, 190); + } + } + + function drawSteps() { + var i = 0; + var cx = 150 + 45; + var cy = 130; + var r = 34; + + var steps = getSteps(); + + if (trip.getTripState() == true) + steps = trip.getTrip(steps); + + if (prevSteps == steps) + return; + + prevSteps = steps; + + var percent = steps / 10000; + + if (percent > 1) percent = 1; + + var startrot = 0 - 180; + var midrot = -180 - (360 * percent); + var endrot = -360 - 180; + + g.setColor(0x07FF); // light cyan + + // draw guauge + for (i = startrot; i > midrot; i -= 3) { + x = cx + r * Math.sin(radians(i)); + y = cy + r * Math.cos(radians(i)); + g.fillCircle(x,y,3); + } + + // change the remaining color to RED if battery is below 25% + if (E.getBattery() > 25) { + //g.setColor(0x7BEF); // grey + g.setColor(0x000D); // dark navy + } else { + g.setColor(0xF800); // red + } + + // draw remainder of guage in grey or red + for (i = midrot - 12; i > endrot + 12; i -= 3) { + x = cx + r * Math.sin(radians(i)); + y = cy + r * Math.cos(radians(i)); + g.fillCircle(x,y,3); + } + } + + function getSteps() { + if (stepsWidget() === undefined) + return "E-STEPS"; + + return stepsWidget().getSteps(); + } + + function stepsWidget() { + if (WIDGETS.activepedom !== undefined) { + return WIDGETS.activepedom; + } else if (WIDGETS.wpedom !== undefined) { + return WIDGETS.wpedom; + } + return undefined; + } + + return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer, + onButtonShort:onButtonShort, onButtonLong:onButtonLong}; + } + + return getFace; +})(); diff --git a/apps/launch/ChangeLog b/apps/launch/ChangeLog index 7e7ea65ab..b56c9f6bb 100644 --- a/apps/launch/ChangeLog +++ b/apps/launch/ChangeLog @@ -2,3 +2,5 @@ 0.02: Only store relevant app data (saves RAM when many apps) 0.03: Allow scrolling to wrap around (fix #382) 0.04: Now displays widgets +0.05: Use g.theme for colours +0.06: Use Bangle.setUI for buttons diff --git a/apps/launch/app.js b/apps/launch/app.js index 9795d8901..ab1a89fc0 100644 --- a/apps/launch/app.js +++ b/apps/launch/app.js @@ -12,55 +12,53 @@ var menuScroll = 0; var menuShowing = false; function drawMenu() { - g.setFont("6x8",2); - g.setFontAlign(-1,0); - var n = 3; + g.reset().setFont("6x8",2).setFontAlign(-1,0); + var w = g.getWidth(); + var h = g.getHeight(); + var m = w/2; + var n = (h-48)/64; if (selected>=n+menuScroll) menuScroll = 1+selected-n; if (selectedn+menuScroll) ? -1 : 0); - g.fillPoly([120,233,106,219,134,219]); + g.setColor(menuScroll ? g.theme.fg : g.theme.bg); + g.fillPoly([m,6,m-14,20,m+14,20]); + g.setColor((apps.length>n+menuScroll) ? g.theme.fg : g.theme.bg); + g.fillPoly([m,h-7,m-14,h-21,m+14,h-21]); // draw - g.setColor(-1); + g.setColor(g.theme.fg); for (var i=0;i=apps.length) selected = 0; - drawMenu(); -}, BTN3, {repeat:true}); -setWatch(function() { // run - if (!apps[selected].src) return; - if (require("Storage").read(apps[selected].src)===undefined) { - E.showMessage("App Source\nNot found"); - setTimeout(drawMenu, 2000); +Bangle.setUI("updown",dir=>{ + if (dir) { + selected += dir; + if (selected<0) selected = apps.length-1; + if (selected>=apps.length) selected = 0; + drawMenu(); } else { - E.showMessage("Loading..."); - load(apps[selected].src); + if (!apps[selected].src) return; + if (require("Storage").read(apps[selected].src)===undefined) { + E.showMessage("App Source\nNot found"); + setTimeout(drawMenu, 2000); + } else { + E.showMessage("Loading..."); + load(apps[selected].src); + } } -}, BTN2, {repeat:true,edge:"falling"}); +}); Bangle.loadWidgets(); Bangle.drawWidgets(); diff --git a/apps/launchb2/ChangeLog b/apps/launchb2/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/launchb2/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/launchb2/app.js b/apps/launchb2/app.js new file mode 100644 index 000000000..56f3fd696 --- /dev/null +++ b/apps/launchb2/app.js @@ -0,0 +1,67 @@ +var s = require("Storage"); +var apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="clock" || !app.type)); +apps.sort((a,b)=>{ + var n=(0|a.sortorder)-(0|b.sortorder); + if (n) return n; // do sortorder first + if (a.nameb.name) return 1; + return 0; +}); +var APPH = 64; +var menuScroll = 0; +var menuShowing = false; +var w = g.getWidth(); +var h = g.getHeight(); +var n = Math.ceil((h-24)/APPH); +var menuScrollMax = APPH*apps.length - (h-24); + +apps.forEach(app=>{ + if (app.icon) + app.icon = s.read(app.icon); // should just be a link to a memory area +}); + +function drawApp(i) { + var y = 24+i*APPH-menuScroll; + var app = apps[i]; + if (!app || y<-APPH || y>=g.getHeight()) return; + g.setFont("6x8",2).setFontAlign(-1,0).drawString(app.name,64,y+32); + if (app.icon) try {g.drawImage(app.icon,8,y+8);} catch(e){} +} + +function drawMenu() { + g.reset().clearRect(0,24,w-1,h-1); + g.setClipRect(0,24,g.getWidth()-1,g.getHeight()-1); + for (var i=0;i{ + var dy = e.dy; + if (menuScroll - dy < 0) + dy = menuScroll; + if (menuScroll - dy > menuScrollMax) + dy = menuScroll - menuScrollMax; + if (!dy) return; + g.reset().setClipRect(0,24,g.getWidth()-1,g.getHeight()-1); + g.scroll(0,dy); + menuScroll -= dy; + if (e.dy < 0) drawApp(Math.floor((menuScroll+24)/APPH)+n-1); + else drawApp(Math.floor((menuScroll+24)/APPH)); + g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1); +}); +Bangle.on("touch",(_,e)=>{ + if (e.y<20) return; + var i = Math.floor((e.y+menuScroll-24) / APPH); + var app = apps[i]; + if (!app) return; + if (!app.src || require("Storage").read(app.src)===undefined) { + E.showMessage("App Source\nNot found"); + setTimeout(drawMenu, 2000); + } else { + E.showMessage("Loading..."); + load(app.src); + } +}); +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/launchb2/app.png b/apps/launchb2/app.png new file mode 100644 index 000000000..8b4e6caa2 Binary files /dev/null and b/apps/launchb2/app.png differ diff --git a/apps/marioclock/mario-clock-screen-shot.png b/apps/marioclock/mario-clock-screen-shot.png old mode 100755 new mode 100644 diff --git a/apps/miclock/clock-mixed.png b/apps/miclock/clock-mixed.png old mode 100755 new mode 100644 diff --git a/apps/miclock2/clock-mixed.png b/apps/miclock2/clock-mixed.png old mode 100755 new mode 100644 diff --git a/apps/minionclk/ChangeLog b/apps/minionclk/ChangeLog old mode 100755 new mode 100644 diff --git a/apps/minionclk/app-icon.js b/apps/minionclk/app-icon.js old mode 100755 new mode 100644 diff --git a/apps/minionclk/app.js b/apps/minionclk/app.js old mode 100755 new mode 100644 diff --git a/apps/minionclk/minionclk.png b/apps/minionclk/minionclk.png old mode 100755 new mode 100644 diff --git a/apps/multiclock/ChangeLog b/apps/multiclock/ChangeLog index c02e390b2..2f27f7f28 100644 --- a/apps/multiclock/ChangeLog +++ b/apps/multiclock/ChangeLog @@ -10,3 +10,4 @@ 0.10: Added GPS and Grid Ref clock faces 0.11: Updated Pedometer clock to retrieve steps from either wpedom or activepedom 0.12: Removed GPS and Grid Ref clock faces, superceded by GPS setup and Walkers Clock +0.13: Localised digi.js and timdat.js \ No newline at end of file diff --git a/apps/multiclock/digi.js b/apps/multiclock/digi.js index 4422e6b62..0b2ca4aaa 100644 --- a/apps/multiclock/digi.js +++ b/apps/multiclock/digi.js @@ -1,5 +1,7 @@ (() => { +var locale = require("locale"); + function getFace(){ var buf = Graphics.createArrayBuffer(240,92,1,{msb:true}); @@ -19,7 +21,7 @@ function getFace(){ buf.drawString(time,buf.getWidth()/2,0); buf.setFont("6x8",2); buf.setFontAlign(0,-1); - var date = d.toString().substr(0,15); + var date = locale.dow(d, 1) + " " + locale.date(d, 1); buf.drawString(date, buf.getWidth()/2, 70); flip(); } diff --git a/apps/multiclock/timdat.js b/apps/multiclock/timdat.js index ff1bdf000..a4a93a691 100644 --- a/apps/multiclock/timdat.js +++ b/apps/multiclock/timdat.js @@ -1,16 +1,16 @@ (() => { + var locale = require("locale"); + var dayFirst = ["en_GB", "en_IN", "en_NAV", "de_DE", "nl_NL", "fr_FR", "en_NZ", "en_AU", "de_AT", "en_IL", "es_ES", "fr_BE", "de_CH", "fr_CH", "it_CH", "it_IT", "tr_TR", "pt_BR", "cs_CZ", "pt_PT"]; + var withDot = ["de_DE", "nl_NL", "de_AT", "de_CH", "hu_HU", "cs_CZ", "sl_SI"]; + function getFace(){ var lastmin=-1; function drawClock(){ var d=Date(); if (d.getMinutes()==lastmin) return; - d=d.toString().split(' '); - var min=d[4].substr(3,2); - var sec=d[4].substr(-2); - var tm=d[4].substring(0,5); - var hr=d[4].substr(0,2); - lastmin=min; + var tm=d.toString().split(' ')[4].substring(0,5); + lastmin=d.getMinutes(); g.reset(); g.clearRect(0,24,239,239); var w=g.getWidth(); @@ -19,7 +19,16 @@ g.drawString(tm,4+(w-g.stringWidth(tm))/2,64); g.setFontVector(36); g.setColor(0x07ff); - var dt=d[0]+" "+d[1]+" "+d[2];//+" "+d[3]; + var dt=locale.dow(d, 1) + " "; + if (dayFirst.includes(locale.name)) { + dt+=d.getDate(); + if (withDot.includes(locale.name)) { + dt+="."; + } + dt+=" " + locale.month(d, 1); + } else { + dt+=locale.month(d, 1) + " " + d.getDate(); + } g.drawString(dt,(w-g.stringWidth(dt))/2,160); g.flip(); } diff --git a/apps/mysticclock/ChangeLog b/apps/mysticclock/ChangeLog new file mode 100644 index 000000000..34fe53627 --- /dev/null +++ b/apps/mysticclock/ChangeLog @@ -0,0 +1 @@ +1.00: First published version. diff --git a/apps/mysticclock/README.md b/apps/mysticclock/README.md new file mode 100644 index 000000000..fd5bbb431 --- /dev/null +++ b/apps/mysticclock/README.md @@ -0,0 +1,40 @@ +# Mystic Clock for Bangle.js + +A retro-inspired watchface featuring time, date, and an interactive data display line. + +## Features + +- 24 or 12-hour time (adjustable via the Settings menu) +- Variable colors (also in the Settings) +- Interactive data display line (use upper and lower watch-buttons to rotate between values) +- Cover watch screen with your hand to put it to sleep (the watch, not your hand) +- International localization of date (which can be disabled via the Settings if memory becomes an issue) + +The interactive line rotates between the following items: + +- Current time zone +- Battery charge level +- Device ID (derived from the last 4 of the MAC) +- Memory usage +- Firmware version + + +## Inspirations + +- [CLI Clock](https://github.com/espruino/BangleApps/tree/master/apps/cliock) +- [Dev Clock](https://github.com/espruino/BangleApps/tree/master/apps/dclock) +- [Digital Clock](https://github.com/espruino/BangleApps/tree/master/apps/digiclock) +- [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock) +- [Simplest Clock](https://github.com/espruino/BangleApps/tree/master/apps/simplest) + +Icon adapted from [Public Domain Vectors](https://publicdomainvectors.org/en/free-clipart/Digital-clock-display-vector-image/10845.html). + + +## Changelog + +- 1.00: First published version. (June 2021) + + +## Author + +Eric Wooodward https://itsericwoodward.com/ diff --git a/apps/mysticclock/mystic-clock-app.js b/apps/mysticclock/mystic-clock-app.js new file mode 100644 index 000000000..22c2c8982 --- /dev/null +++ b/apps/mysticclock/mystic-clock-app.js @@ -0,0 +1,215 @@ +/** + * Mystic Clock for Bangle.js + * + * + Original Author: Eric Wooodward https://itsericwoodward.com/ + * + see README.md for details + */ + +/* jshint esversion: 6 */ + +const timeFontSize = 6; +const dataFontSize = 2; +const font = "6x8"; + +const xyCenter = g.getWidth() / 2; + +const yposTime = 75; +const yposDate = 125; +const yposSymbol = 160; +const yposInfo = 220; + +const settings = require('Storage').readJSON('mysticclock.json', 1) || {}; +const colors = ['white', 'blue', 'green', 'purple', 'red', 'teal', 'other']; +const color = settings.color ? colors[settings.color] : 0; + +const infoData = { + '*GMT_MODE': { + calc: () => (new Date()).toString().split(" ")[5], + }, + BATT_MODE: { + calc: () => `BATT: ${E.getBattery()}%`, + }, + ID_MODE: { + calc: () => { + const val = NRF.getAddress().split(":"); + return `ID: ${val[4]}${val[5]}`; + }, + }, + MEM_MODE: { + calc: () => { + const val = process.memory(); + return `MEM: ${Math.round(val.usage * 100 / val.total)}%`; + }, + }, + VER_MODE: { + calc: () => `FW: ${process.env.VERSION}`, + }, +}; +const infoList = Object.keys(infoData).sort(); +let infoMode = infoList[0]; + +function setColor() { + const colorCommands = { + white: () => g.setColor(1, 1, 1), + blue: () => g.setColor(0, 0, 1), + green: () => g.setColor(0, 1, 0), + purple: () => g.setColor(1, 0, 1), + red: () => g.setColor(1, 0, 0), + teal: () => g.setColor(0, 1, 1), + other: () => g.setColor(1, 1, 0) + }; + + // default if value unknown + if (!color || !colorCommands[color]) return colorCommands.white(); + return colorCommands[color](); +} + +function getLocale() { + return require('locale'); +} + +function drawClock() { + + // default draw styles + g.reset(); + + // drawSting centered + g.setFontAlign(0, 0); + + // setup color + setColor(); + + // get date + const d = new Date(); + const dLocal = d.toString().split(" "); + + const useLocale = !settings.useLocale; + + const minutes = (`0${d.getMinutes()}`).substr(-2); + const seconds = (`0${d.getSeconds()}`).substr(-2); + + let hours = (`0${d.getHours()}`).substr(-2); + let meridian = ""; + + if (settings.use12Hour) { + hours = parseInt(hours, 10); + meridian = 'AM'; + if (hours === 0) { + hours = 12; + } + else if (hours >= 12) { + meridian = 'PM'; + if (hours > 12) hours -= 12; + } + hours = (' ' + hours).substr(-2); + } + + g.setFont(font, timeFontSize); + g.drawString(`${hours}${(d.getSeconds() % 2) ? ' ' : ':'}${minutes}`, xyCenter - 15, yposTime, true); + g.setFont(font, dataFontSize); + + if (settings.use12Hour) { + g.drawString(seconds, xyCenter + 97, yposTime - 10, true); + g.drawString(meridian, xyCenter + 97, yposTime + 10, true); + } + else { + g.drawString(seconds, xyCenter + 97, yposTime + 10, true); + } + + // draw DoW, name of month, date, year + g.setFont(font, dataFontSize); + g.drawString([ + useLocale ? getLocale().dow(d, 1) : dLocal[0], + useLocale ? getLocale().month(d, 1) : dLocal[1], + d.getDate(), + d.getFullYear() + ].join(" "), xyCenter, yposDate, true); + +} + +function drawInfo() { + if (infoData[infoMode] && infoData[infoMode].calc) { + // clear info + g.setColor(0, 0, 0); + g.fillRect(0, yposInfo - 8, 239, yposInfo + 25); + + // draw info + g.setFont(font, dataFontSize); + setColor(); + g.drawString((infoData[infoMode].calc()), xyCenter, yposInfo, true); + } +} + +function drawImage() { + setColor(); + g.drawPoly([xyCenter - 100, yposSymbol, xyCenter + 100, yposSymbol, xyCenter, yposSymbol + 30], true); +} + +function drawAll() { + drawClock(); + drawInfo(); + drawImage(); +} + +function nextInfo() { + let idx = infoList.indexOf(infoMode); + if (idx > -1) { + if (idx === infoList.length - 1) infoMode = infoList[0]; + else infoMode = infoList[idx + 1]; + } +} + +function prevInfo() { + let idx = infoList.indexOf(infoMode); + if (idx > -1) { + if (idx === 0) infoMode = infoList[infoList.length - 1]; + else infoMode = infoList[idx - 1]; + } +} + + +let secondInterval; + +// handle LCD power state change +Bangle.on('lcdPower', on => { + + // stop running when screen turns off + if (secondInterval) clearInterval(secondInterval); + secondInterval = undefined; + + // start running + if (on) { + secondInterval = setInterval(drawAll, 1000); + drawAll(); // draw immediately + } +}); + +// cover screen to put it to sleep +Bangle.on('touch', (button) => { + if (button === 3 && Bangle.isLCDOn()) Bangle.setLCDPower(false); +}); + +// clean app screen +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +// if screen already active, draw now and start interval +if (Bangle.isLCDOn()) { + secondInterval = setInterval(drawAll, 1000); + drawAll(); // draw immediately +} + +// show launcher when middle button pressed +setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); + +// rotate through info when the buttons are pressed +setWatch(() => { + nextInfo(); + drawAll(); +}, BTN3, { repeat: true }); + +setWatch(() => { + prevInfo(); + drawAll(); +}, BTN1, { repeat: true }); diff --git a/apps/mysticclock/mystic-clock-icon.js b/apps/mysticclock/mystic-clock-icon.js new file mode 100644 index 000000000..7415fccd5 --- /dev/null +++ b/apps/mysticclock/mystic-clock-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBIf4A/AH4A/AH4A/AH4ALs1msADCA4MGAgQDBBYIAGg93u92s4DBuEAAYN3swDCC5AhBuwMBg4XBuwEBs4dCC49nHgNwCQREBCYNnEYYXHHQQvBAAJZBAgRPEC5IOCu0GM4YLCuCGDAAREBHwtnJ41gDQIXEOAQvBDoZ7CuwjCWwimTJgLCFZojWEbwbWIAH4A/AH4A/AH4A/AH4AFA")) diff --git a/apps/mysticclock/mystic-clock-settings.js b/apps/mysticclock/mystic-clock-settings.js new file mode 100644 index 000000000..2fa0c49c5 --- /dev/null +++ b/apps/mysticclock/mystic-clock-settings.js @@ -0,0 +1,41 @@ +// make sure to enclose the function in parentheses +(function (back) { + + const settings = require('Storage').readJSON('mysticclock.json',1)||{}; + const colors = ['White', 'Blue', 'Green', 'Purple', 'Red', 'Teal', 'Yellow']; + const offon = ['Off','On']; + const onoff = ['On','Off']; + + function save(key, value) { + settings[key] = value; + require('Storage').writeJSON('mysticclock.json',settings); + } + + const appMenu = { + '': {'title': 'Clock Settings'}, + '< Back': back, + 'Color': { + value: 0|settings['color'], + min:0, + max:6, + format: m => colors[m], + onchange: m => {save('color', m)} + }, + '12 Hour Clock': { + value: 0|settings['use12Hour'], + min:0, + max:1, + format: m => offon[m], + onchange: m => {save('use12Hour', m)} + }, + 'Use Locale': { + value: 0|settings['useLocale'], + min:0, + max:1, + format: m => onoff[m], + onchange: m => {save('useLocale', m)} + } + }; + E.showMenu(appMenu) + +}) diff --git a/apps/mysticclock/mystic-clock.png b/apps/mysticclock/mystic-clock.png new file mode 100644 index 000000000..915e2ee32 Binary files /dev/null and b/apps/mysticclock/mystic-clock.png differ diff --git a/apps/mysticdock/ChangeLog b/apps/mysticdock/ChangeLog new file mode 100644 index 000000000..34fe53627 --- /dev/null +++ b/apps/mysticdock/ChangeLog @@ -0,0 +1 @@ +1.00: First published version. diff --git a/apps/mysticdock/README.md b/apps/mysticdock/README.md new file mode 100644 index 000000000..09e81ba09 --- /dev/null +++ b/apps/mysticdock/README.md @@ -0,0 +1,43 @@ +# Mystic Dock for Bangle.js + +A retro-inspired dockface that displays the current time and battery charge while plugged in, and which features an interactive mode that shows the time, date, and a rotating data display line. + +## Features + +- Screensaver-like dock mode while charging (displays the current time for 8 seconds and a blank screen for 2, changing text placement with each draw) +- 24 or 12-hour time (adjustable via the Settings menu) +- Variable colors (also in the Settings) +- Interactive watchface display (use upper and lower watch-buttons to activate it and rotate between values at the bottom) +- International localization of watchface date (which can be disabled via the Settings if memory becomes an issue) +- Automatic watchface reload when unplugged (toggleable via the Settings menu) +- Rotates display 90 degrees if it detects it is sideways (for use in a charging dock) + +When in interactive display mode, the bottom line rotates between the following items: + +- Current time zone +- Battery charge level +- Device ID (derived from the last 4 of the MAC) +- Memory usage +- Firmware version + + +## Inspirations + +- [Bluetooth Dock](https://github.com/espruino/BangleApps/tree/master/apps/bluetoothdock) +- [CLI Clock](https://github.com/espruino/BangleApps/tree/master/apps/cliock) +- [Dev Clock](https://github.com/espruino/BangleApps/tree/master/apps/dclock) +- [Digital Clock](https://github.com/espruino/BangleApps/tree/master/apps/digiclock) +- [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock) +- [Simplest Clock](https://github.com/espruino/BangleApps/tree/master/apps/simplest) + +Icon adapted from [this one](https://publicdomainvectors.org/en/free-clipart/Digital-clock-display-vector-image/10845.html) and [this one](https://publicdomainvectors.org/en/free-clipart/Vector-image-of-power-manager-icon/20141.html) from [Public Domain Vectors](https://publicdomainvectors.org). + + +## Changelog + +- 1.00: First published version. (June 2021) + + +## Author + +Eric Wooodward https://itsericwoodward.com/ diff --git a/apps/mysticdock/mystic-dock-app.js b/apps/mysticdock/mystic-dock-app.js new file mode 100644 index 000000000..2e6fdafc5 --- /dev/null +++ b/apps/mysticdock/mystic-dock-app.js @@ -0,0 +1,247 @@ +/** + * Mystic Dock for Bangle.js + * + * + Original Author: Eric Wooodward https://itsericwoodward.com/ + * + see README.md for details + */ + +/* jshint esversion: 6 */ + +const timeFontSize = 6; +const dataFontSize = 2; +const font = "6x8"; + +const xyCenter = g.getWidth() / 2; + +const ypos = [ + 45, // Time + 105, // Date + 145, // Symbol + 210 // Info +]; + +const settings = require('Storage').readJSON('mysticdock.json', 1) || + require('Storage').readJSON('mysticclock.json', 1) || {}; +const colors = ['white', 'blue', 'green', 'purple', 'red', 'teal', 'other']; +const color = settings.color ? colors[settings.color] : 0; + +const yposMax = 190; +const yposMin = 60; +let y = yposMax; + +let lastButtonPressTime; +let wasInActiveMode = false; + + +const infoData = { + '*GMT_MODE': { + calc: () => (new Date()).toString().split(" ")[5], + }, + BATT_MODE: { + calc: () => `BATT: ${E.getBattery()}%`, + }, + ID_MODE: { + calc: () => { + const val = NRF.getAddress().split(":"); + return `ID: ${val[4]}${val[5]}`; + }, + }, + MEM_MODE: { + calc: () => { + const val = process.memory(); + return `MEM: ${Math.round(val.usage * 100 / val.total)}%`; + }, + }, + VER_MODE: { + calc: () => `FW: ${process.env.VERSION}`, + }, +}; +const infoList = Object.keys(infoData).sort(); +let infoMode = infoList[0]; + + +function setColor() { + const colorCommands = { + white: () => g.setColor(1, 1, 1), + blue: () => g.setColor(0, 0, 1), + green: () => g.setColor(0, 1, 0), + purple: () => g.setColor(1, 0, 1), + red: () => g.setColor(1, 0, 0), + teal: () => g.setColor(0, 1, 1), + other: () => g.setColor(1, 1, 0) + }; + + // default if value unknown + if (!color || !colorCommands[color]) return colorCommands.white(); + return colorCommands[color](); +} + + +function drawInfo() { + if (infoData[infoMode] && infoData[infoMode].calc) { + // clear info + g.setColor(0, 0, 0); + g.fillRect(0, ypos[3] - 8, 239, ypos[3] + 25); + + // draw info + g.setFont(font, dataFontSize); + setColor(); + g.drawString((infoData[infoMode].calc()), xyCenter, ypos[3], true); + } +} + +function drawImage() { + setColor(); + g.drawPoly([xyCenter - 100, ypos[2], xyCenter + 100, ypos[2], xyCenter, ypos[2] + 30], true); +} + +function drawClock() { + + // default draw styles + g.reset(); + + // get date + const d = new Date(); + const dLocal = d.toString().split(" "); + + const minutes = (`0${d.getMinutes()}`).substr(-2); + const seconds = (`0${d.getSeconds()}`).substr(-2); + + const useLocale = !settings.useLocale; + + let hours = (`0${d.getHours()}`).substr(-2); + let meridian = ""; + + if (d.getSeconds() % 10 === 0) { + y = Math.floor(Math.random() * (yposMax - yposMin)) + yposMin; + } + + // drawSting centered + g.setFontAlign(0, 0); + + // setup color + setColor(); + + if (settings.use12Hour) { + hours = parseInt(hours, 10); + meridian = 'AM'; + if (hours === 0) { + hours = 12; + } + else if (hours >= 12) { + meridian = 'PM'; + if (hours > 12) hours -= 12; + } + hours = (' ' + hours).substr(-2); + } + + g.setFont(font, timeFontSize); + + if (lastButtonPressTime && ((d.getTime() - lastButtonPressTime) / 1000) < 5) { + + // clear screen when switching modes + if (!wasInActiveMode) { + g.clear(); + wasInActiveMode = true; + } + + // draw clock in center w/ seconds + // show date (locale'd, based on settings) + // show info line below it + g.drawString(`${hours}${(d.getSeconds() % 2) ? ' ' : ':'}${minutes}`, xyCenter - 15, ypos[0], true); + g.setFont(font, dataFontSize); + + if (settings.use12Hour) { + g.drawString(seconds, xyCenter + 97, ypos[0] - 10, true); + g.drawString(meridian, xyCenter + 97, ypos[0] + 10, true); + } + else { + g.drawString(seconds, xyCenter + 97, ypos[0] + 10, true); + } + + // draw DoW, name of month, date, year + g.setFont(font, dataFontSize); + g.drawString([ + useLocale ? require('locale').dow(d, 1) : dLocal[0], + useLocale ? require('locale').month(d, 1) : dLocal[1], + d.getDate(), + d.getFullYear() + ].join(' '), xyCenter, ypos[1], true); + + drawInfo(); + drawImage(); + } + else if (d.getSeconds() % 10 === 8) { + g.clear(); + wasInActiveMode = false; + } + else if (d.getSeconds() % 10 !== 9) { + // clear screen when switching modes + if (wasInActiveMode) { + g.clear(); + wasInActiveMode = false; + } + g.drawString(`${hours}${(d.getSeconds() % 2) ? ' ' : ':'}${minutes}`, xyCenter - (settings.use12Hour ? 15 : 0), y, true); + g.setFont(font, dataFontSize); + if (settings.use12Hour) { + g.drawString(meridian, xyCenter + 97, y + 10, true); + } + g.drawString(`BATT: ${E.getBattery() === 100 ? '100' : ('0' + E.getBattery()).substr(-2)}%`, xyCenter, y + 35, true); + } + + g.flip(); +} + + +function nextInfo() { + lastButtonPressTime = Date.now(); + let idx = infoList.indexOf(infoMode); + + if (idx > -1) { + if (idx === infoList.length - 1) infoMode = infoList[0]; + else infoMode = infoList[idx + 1]; + } +} + + +function prevInfo() { + lastButtonPressTime = Date.now(); + let idx = infoList.indexOf(infoMode); + + if (idx > -1) { + if (idx === 0) infoMode = infoList[infoList.length - 1]; + else infoMode = infoList[idx - 1]; + } +} + + +if (Bangle.getAccel().x < -0.7) { + g.setRotation(3); // assume watch in charge cradle +} + +g.clear(); + +setInterval(drawClock, 1000); +drawClock(); + +if (Bangle.isCharging()) { + Bangle.on("charging", isCharging => { + const reloadOnUplug = !settings.reloadOnUplug; + + if (!isCharging && reloadOnUplug) load(); + }); +} + +// show launcher when middle button pressed +setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); + +// change to "active mode" and rotate through info when the buttons are pressed +setWatch(() => { + nextInfo(); + drawClock(); +}, BTN3, { repeat: true }); + +setWatch(() => { + prevInfo(); + drawClock(); +}, BTN1, { repeat: true }); diff --git a/apps/mysticdock/mystic-dock-boot.js b/apps/mysticdock/mystic-dock-boot.js new file mode 100644 index 000000000..7cb7fa8a4 --- /dev/null +++ b/apps/mysticdock/mystic-dock-boot.js @@ -0,0 +1 @@ +Bangle.on("charging", isCharging => { if (isCharging) load("mysticdock.app.js"); }); diff --git a/apps/mysticdock/mystic-dock-icon.js b/apps/mysticdock/mystic-dock-icon.js new file mode 100644 index 000000000..527825dd7 --- /dev/null +++ b/apps/mysticdock/mystic-dock-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBIf4A6g93u9gs4DCBBIAFu9ms9wAYYIJAAt2FAN2BYMHEwIIIAAkGBQV3AYNns1mBAwXGg4KCIgYTEBAZ2JCYQABBBIXJQoRcCBA0GDQpPCBAUGuwTBBAwfCUwgTDMoVmBA8GQIIXGWoJ9DBA4vHAAIOBcoYIHC4xqCCQR2BBBEGJAKSGAH4Adb4SIBDCYXCUwQwVDCjJCXYS/CDh4SCAAoxPDA72CPaQdCTB57CLgQYCGCIdFJJ4QFTIQXUGwpHQJAapQI4qQPCIqtDVCQECMCR5BJgN2bSArCuACCbSIRCIobZQOgZMCgx4OJIjvCCyAYCCYJMBYB4zHC6oA/AE4=")) diff --git a/apps/mysticdock/mystic-dock-settings.js b/apps/mysticdock/mystic-dock-settings.js new file mode 100644 index 000000000..7bfda1c0f --- /dev/null +++ b/apps/mysticdock/mystic-dock-settings.js @@ -0,0 +1,48 @@ +// make sure to enclose the function in parentheses +(function (back) { + + const settings = require('Storage').readJSON('mysticdock.json',1)||{}; + const colors = ['White', 'Blue', 'Green', 'Purple', 'Red', 'Teal', 'Yellow']; + const offon = ['Off','On']; + const onoff = ['On','Off']; + + function save(key, value) { + settings[key] = value; + require('Storage').writeJSON('mysticdock.json',settings); + } + + const appMenu = { + '': {'title': 'Dock Settings'}, + '< Back': back, + 'Color': { + value: 0|settings['color'], + min:0, + max:6, + format: m => colors[m], + onchange: m => {save('color', m)} + }, + '12 Hour Clock': { + value: 0|settings['use12Hour'], + min:0, + max:1, + format: m => offon[m], + onchange: m => {save('use12Hour', m)} + }, + 'Reload on Unplug': { + value: 0|settings['reloadOnUplug'], + min:0, + max:1, + format: m => onoff[m], + onchange: m => {save('reloadOnUplug', m)} + }, + 'Use Locale': { + value: 0|settings['useLocale'], + min:0, + max:1, + format: m => onoff[m], + onchange: m => {save('useLocale', m)} + }, + }; + E.showMenu(appMenu) + +}) diff --git a/apps/mysticdock/mystic-dock.png b/apps/mysticdock/mystic-dock.png new file mode 100644 index 000000000..4c0dce770 Binary files /dev/null and b/apps/mysticdock/mystic-dock.png differ diff --git a/apps/mywelcome/app.js b/apps/mywelcome/app.js index 23cdd0d49..9d4a54928 100644 --- a/apps/mywelcome/app.js +++ b/apps/mywelcome/app.js @@ -11,11 +11,9 @@ function animate(seq,period) { // Fade in to FG color with angled lines function fade(col, callback) { var n = 0; - function f() { + 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); - } + 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); @@ -25,19 +23,22 @@ function fade(col, callback) { } -var scenes = [ - function() { +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")); - },function() { + } + if (n==1) return function() { g.clear(1); g.setFont("4x6",2); var n=0; + var l = Bangle.getLogo(); var i = setInterval(function() { n+=0.04; g.setColor(n,n,n); - g.drawImage(Bangle.getLogo(),(240-222)/2,(240-100)/2); + g.drawImage(l,(240-222)/2,(240-100)/2); if (n>=1) { clearInterval(i); setTimeout(()=>g.drawString("Open",34,144), 500); @@ -45,7 +46,8 @@ var scenes = [ setTimeout(()=>g.drawString("Smart Watch",34,168), 1500); } },50); - },function() { + }; + if (n==2) return function() { var img = require("heatshrink").decompress(atob("ptRxH+qYAfvl70mj5gAC0ekvd8FkAAdz3HJAYAH4+eJXWkJJYAF0hK2vfNJaIAB5t7S3fN5/V6wAD6vOTg9SumXy2W3QAB3eXul2JdnO63XAApPEVYvAJQIACJoRQDzBLoJQ3W5/NIwr4GJohMFAAROgJYvVJQiPGABZNN3bsdvYyESwnWJSIAC3RNM3V1JjZAES4nVJSYAB4xMNJrbkE56WD5xLVdB5NbFofNJbgABJh26qREPrFXrlbAAWjFgfWJgRLaTQhMLy5KNJINhsJLDrYrD5xLC6pLa5nGTR7oLq9bJQJMKTAXWJbbnR3RLJSoRMHv4pC5rkec6SaIrBLGw2r2XW1epcoqYeJiOXJYziEsOH2RBBw7lF56Yg5nGc6FScZOGJQPX2TmDFIfVTEBMSc4hLEw5KB6+rsJMH63X6pMf5hMQzBLCq5LD1ZLEJhTlfJiWXTA2GJYpMIcwPNc2O6TAuGRIPX1igDJg/PJmyYDcgXWwxMH1ApC53XcsHAJiVYcg2HJYZME0YpC5vWJkhLNJgLlDTAeFJhF/FQfVJkG6JiGXcomyJgOrJYhMErYqD53NJj7lRzBMDcoeGJhzoBJb3GJiN1qZBCJgWyJYpNF1LigAAXAJiNSJgzlGJgt/JkZLRy9TJgeHJhznFcuSZGw5MHJomjcuhLBqdcJiSaiTChMV1CYxy5LCqdXIAWy6+rJhCalTCN2JgdYH4WHJiGpTF7kDc43W2RMJTUZLQzBLFc4mr6+GJh2jTFmXJYyaEwuyc5Sag4xLZTQmG2WFJhxNaJYZMLJZSaEJoOHTR9/Ja+6JbdTqRNETRRNF1JLV4BLcAANYI5ToK1BLYJhWYJZwABq5NoJZ91JaAABdAZNS0ZLey9SJaRNYv5KM426JZmXuxKUJrKcL0lTzBLKzBKYJrVXvfGSol7EYWXJI27zF1JLQADq5NUrgYB4wAEEIV0comXI7wAFrCcPJgYWBTIIAETIN2JYmWuhMkdSdYCgOeJgueqRLFyzhfTi9bq4TC45MF49TuuXJlpONcogAC0hKB0gHDvZMEqRMpAANSq9crlbJAYADqwRDxGk0mIA4eCTQOeveXJdYAHqxNFdAeIAAQGCrOI0oHEAGVXTRJMGvgGCwRM7TAZMHwQGCvhM1rBMERIhMGAwdZJmtSqVTwNcwJEDJg19cvIADa4d9JhANDJnSLHJgrl6AAhFFAwpZDegjn7vhMGcvwABrJAFJgjl/TQpBBI4jl/AAN8TQhHDcv4ADcJBMDvpM+IYaeDAAhL+qd9SgycEJn7iEAA18Jf7nEcv4AIrJLIcv6aMcv4ADvhMHrJJ/AAbl/c6ZM/AAt9cv7nSIv7nLcv4AHrLl/TRpJBvgnjA==")); g.reset(); g.setBgColor("#6633ff"); @@ -76,7 +78,8 @@ var scenes = [ },20); },3500); - },function() { + }; + if (n==3) return function() { g.reset(); g.setBgColor("#ffa800");g.clear(); g.setFont("6x8",2); @@ -91,8 +94,8 @@ var scenes = [ ()=>g.drawString("2",200,120), ()=>g.drawString("3",200,200) ],200); - }, - function() { + }; + if (n==4) return function() { g.reset(); g.setBgColor("#00a8ff");g.clear(); g.setFontAlign(0,0); @@ -101,8 +104,8 @@ var scenes = [ g.setFontAlign(-1,-1); g.setFont("6x8",2); g.drawString("Move up\nin menus\n\nTurn Bangle.js on\nif it was off", 20,40); - }, - function() { + }; + if (n==5) return function() { g.reset(); g.setBgColor("#00a8ff");g.clear(); g.setFontAlign(0,0); @@ -111,8 +114,8 @@ var scenes = [ g.setFontAlign(-1,-1); g.setFont("6x8",2); g.drawString("Select menu\nitem\n\nLaunch app\nwhen watch\nis showing", 20,70); - }, - function() { + }; + if (n==6) return function() { g.reset(); g.setBgColor("#00a8ff");g.clear(); g.setFontAlign(0,0); @@ -121,8 +124,8 @@ var scenes = [ g.setFontAlign(-1,-1); g.setFont("6x8",2); g.drawString("Move down\nin menus\n\nLong press\nto exit app\nand go back\nto clock", 20,100); - }, - function() { + }; + if (n==7) return function() { g.reset(); g.setBgColor("#ff3300");g.clear(); g.setFontAlign(0,0); @@ -132,8 +135,8 @@ var scenes = [ g.setFontAlign(-1,-1); g.setFont("6x8",2); g.drawString("If Bangle.js\never stops,\nhold buttons\n1 and 2 for\naround six\nseconds.\n\n\n\nBangle.js will\nthen reboot.", 20,20); - }, - function() { + }; + if (n==8) return function() { g.reset(); g.setBgColor("#00a8ff");g.clear(); g.setFont("6x8",2); @@ -150,8 +153,8 @@ var scenes = [ g.drawString("work too. Try now",x,y+=h); g.drawString("to change page.",x,y+=h);} ],300); - }, - function() { + }; + if (n==9) return function() { g.reset(); g.setBgColor("#339900");g.clear(); g.setFont("6x8",2); @@ -168,8 +171,8 @@ var scenes = [ g.drawString("with a Bluetooth",x,y+=h); g.drawString("capable device",x,y+=h);}, ],400); - }, - function() { + }; + if (n==10) return function() { g.reset(); g.setBgColor("#990066");g.clear(); g.setFont("6x8",2); @@ -182,6 +185,7 @@ var scenes = [ g.drawString("banglejs.com",x,y+=h); var rx = 0, ry = 0; + E.defrag(); // rearrange memory to ensure we have space var h = Graphics.createArrayBuffer(96,96,1,{msb:true}); // draw a cube function draw() { @@ -230,8 +234,8 @@ var scenes = [ } setInterval(draw,50); - }, - function() { + }; + if (n==11) return function() { g.reset(); g.setBgColor("#660099");g.clear(); g.setFontAlign(0,0); @@ -248,20 +252,18 @@ var scenes = [ g.drawString("Bangle.js",x,y+=h);} ],400); } -]; +} var sceneNumber = 0; function move(dir) { - if (dir>0 && sceneNumber+1 == scenes.length) return; // at the end - sceneNumber = (sceneNumber+dir)%scenes.length; + if (dir>0 && sceneNumber+1 == SCENE_COUNT) return; // at the end + sceneNumber = (sceneNumber+dir)%SCENE_COUNT; if (sceneNumber<0) sceneNumber=0; clearInterval(); - Bangle.setLCDMode(); - g.clear(); - scenes[sceneNumber](); + getScene(sceneNumber)(); if (sceneNumber>2) { - var l = scenes.length; + var l = SCENE_COUNT; for (var i=0;i -size) setTimeout(anim, 15); } anim(); - Bangle.on("touch", exports.hide); + Bangle.on("touch", exports.dismiss_and_hide); }; /** @@ -162,3 +162,20 @@ exports.hide = function(options) { } anim(); }; + +/** + Calls exports.hide(), but if Gadgetbridge is installed, dismiss through it + instead (which will call call exports.hide() itself). +*/ +exports.dismiss_and_hide = function(options) { + options = options||{}; + if (typeof(options) == "number") { + options = {}; + } + if ("GB" in global) { + options["t"] = "notify-"; + GB(options); + } else { + exports.hide(options); + } +}; diff --git a/apps/numerals/ChangeLog b/apps/numerals/ChangeLog index 2869074a6..f94d719f4 100644 --- a/apps/numerals/ChangeLog +++ b/apps/numerals/ChangeLog @@ -6,3 +6,4 @@ 0.06: Improve rendering of Numeral 1, fix issue with alarms not showing up 0.07: Add date on touch and some improvements (see settings and readme) 0.08: Add new draw styles, tidy up draw functionality +0.09: Tweak for faster rendering diff --git a/apps/numerals/numerals.app.js b/apps/numerals/numerals.app.js index 49a41732e..3c7607eb1 100644 --- a/apps/numerals/numerals.app.js +++ b/apps/numerals/numerals.app.js @@ -22,16 +22,17 @@ var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||fal var _hCol = ["#ff5555","#ffff00","#FF9901","#2F00FF"]; var _mCol = ["#55ff55","#ffffff","#00EFEF","#FFBF00"]; var _rCol = 0; +var scale = g.getWidth()/240; var interval = 0; const REFRESH_RATE = 10E3; var drawFuncs = { fill : function(poly,isHole){ - if (isHole) g.setColor(0); + if (isHole) g.setColor(g.theme.bg); g.fillPoly(poly,true); }, framefill : function(poly,isHole){ var c = g.getColor(); - g.setColor(isHole ? 0 : ((c&0b1111011111011110)>>1)); // 16 bit half bright + g.setColor(isHole ? g.theme.bg : ((c&0b1111011111011110)>>1)); // 16 bit half bright g.fillPoly(poly,true); g.setColor(c); g.drawPoly(poly,true); @@ -48,7 +49,8 @@ var drawFuncs = { }; function translate(tx, ty, p){ - return p.map((x, i)=> x+((i&1)?ty:tx)); + //return p.map((x, i)=> x+((i&1)?ty:tx)); + return g.transformVertices(p, {x:tx,y:ty,scale:scale}); } @@ -57,15 +59,14 @@ if (!settings) { settings = { color:0, drawMode:"fill", - menuButton:24, showDate:0 }; } function drawNum(num,col,x,y,func,funcName){ g.setColor(col); - let tx = x*100+25; - let ty = y*104+32; + let tx = (x*100+25) * scale; + let ty = (y*104+32) * scale; for (let i=0;i0); @@ -98,9 +99,9 @@ function setUpdateInt(set){ if (set) interval=setInterval(draw, REFRESH_RATE); } -Bangle.setLCDMode(); -g.reset().clear(); -setWatch(Bangle.showLauncher, settings.menuButton, {repeat:false,edge:"falling"}); +g.clear(1); +// Show launcher when button pressed +Bangle.setUI("clock"); if (settings.color>0) _rCol=settings.color-1; setUpdateInt(1); draw(); diff --git a/apps/numerals/numerals.settings.js b/apps/numerals/numerals.settings.js index 991abc888..70f6e0d98 100644 --- a/apps/numerals/numerals.settings.js +++ b/apps/numerals/numerals.settings.js @@ -6,14 +6,12 @@ numeralsSettings = { color:0, drawMode:"fill", - menuButton:22, showDate:0 }; updateSettings(); } let numeralsSettings = storage.readJSON('numerals.json',1); if (!numeralsSettings) resetSettings(); - if (numeralsSettings.menuButton===undefined) numeralsSettings.menuButton=22; let dm = ["fill","frame","framefill","thickframe"]; let col = ["rnd","r/g","y/w","o/c","b/y"]; let btn = [[24,"BTN1"],[22,"BTN2"],[23,"BTN3"],[11,"BTN4"],[16,"BTN5"]]; @@ -31,12 +29,6 @@ format: v=>dm[v], onchange: v=> { numeralsSettings.drawMode=dm[v]; updateSettings();} }, - "Menu button": { - value: btn.findIndex(e=>e[0]==numeralsSettings.menuButton), - min:0,max:btn.length-1, - format: v=>btn[v][1], - onchange: v=> { numeralsSettings.menuButton=btn[v][0]; updateSettings();} - }, "Date on touch": { value: 0|numeralsSettings.showDate, min:0,max:1, diff --git a/apps/openstmap/ChangeLog b/apps/openstmap/ChangeLog index 64b39b509..fb5776351 100644 --- a/apps/openstmap/ChangeLog +++ b/apps/openstmap/ChangeLog @@ -3,3 +3,6 @@ 0.03: Show widgets (mainly so we can use the GPS recorder widget) 0.04: Move map rendering to a module (fix #396) 0.05: Show currently active gpsrec GPS trace (fix #395) +0.06: Add support for scrolling, option for 3 bit maps +0.07: Move to 96px tiles - less files (64 -> 25) and speed up rendering +0.08: Update for drag event refactor diff --git a/apps/openstmap/app.js b/apps/openstmap/app.js index 940557361..18c8df74b 100644 --- a/apps/openstmap/app.js +++ b/apps/openstmap/app.js @@ -34,7 +34,7 @@ Bangle.on('GPS',function(f) { g.drawString(txt,120,y1 + 4); drawMarker(); }); -Bangle.setGPSPower(1); +Bangle.setGPSPower(1, "app"); if (HASWIDGETS) { Bangle.loadWidgets(); @@ -49,9 +49,25 @@ if (HASWIDGETS) { redraw(); -setWatch(function() { +function recenter() { if (!fix.fix) return; m.lat = fix.lat; m.lon = fix.lon; redraw(); -}, BTN2, {repeat:true}); +} + +setWatch(recenter, global.BTN2?BTN2:BTN1, {repeat:true}); + +var hasScrolled = false; +Bangle.on('drag',e=>{ + if (e.b) { + g.setClipRect(0,y1,g.getWidth()-1,y2); + g.scroll(e.dx,e.dy); + m.scroll(e.dx,e.dy); + g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1); + hasScrolled = true; + } else if (hasScrolled) { + hasScrolled = false; + redraw(); + } +}); diff --git a/apps/openstmap/custom.html b/apps/openstmap/custom.html index 81a55a4f8..ac046d41d 100644 --- a/apps/openstmap/custom.html +++ b/apps/openstmap/custom.html @@ -32,6 +32,7 @@
+

3 bit