diff --git a/apps/fuzzyw/ChangeLog b/apps/fuzzyw/ChangeLog new file mode 100644 index 0000000000..7b83706bf8 --- /dev/null +++ b/apps/fuzzyw/ChangeLog @@ -0,0 +1 @@ +0.01: First release diff --git a/apps/fuzzyw/README.md b/apps/fuzzyw/README.md new file mode 100644 index 0000000000..906eb167ba --- /dev/null +++ b/apps/fuzzyw/README.md @@ -0,0 +1,26 @@ +# Fuzzy Text Clock + +An imprecise clock for when you're not in a rush. + +This clock is a remake of one of my favourite Pebble watchfaces, Fuzzy Text International. I use this watch for weekends and holidays, when 'within 5 minutes of the actual time' is close enough! + +By default it will use the language set on the watch, go to settings to pick: +* en_GB - English +* en_US - American +* es_ES - Spanish +* fr_FR - French +* no_NO - Norwegian +* sv_SE - Swedish +* de_DE - German + +Most translations are taken from the original Fuzzy Text International code. + +## TODO +* Bold hour word (as the pebble version has) +* Animation when changing time? + +## References +Based on Pebble app Fuzzy Text International: https://github.com/hallettj/Fuzzy-Text-International + +![](fuzzyw-light.png) +![](fuzzyw-dark.png) diff --git a/apps/fuzzyw/fuzzy_strings.json b/apps/fuzzyw/fuzzy_strings.json new file mode 100644 index 0000000000..8594ad5547 --- /dev/null +++ b/apps/fuzzyw/fuzzy_strings.json @@ -0,0 +1,163 @@ +{ + "en_GB":{ + "hours":[ + "midnight", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", "eleven", + "twelve", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", "eleven" + ], + "minutes":[ + "*$1 o'clock", + "five past *$1", + "ten past *$1", + "quarter past *$1", + "twenty past *$1", + "twenty five past *$1", + "half past *$1", + "twenty five to *$2", + "twenty to *$2", + "quarter to *$2", + "ten to *$2", + "five to *$2" + ], + "text_scale":3.5 + }, + "en_US":{ + "hours":[ + "midnight", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", "eleven", + "twelve", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", "eleven" + ], + "minutes":[ + "*$1 o'clock", + "five after *$1", + "ten after *$1", + "quarter after *$1", + "twenty after *$1", + "twenty five after *$1", + "half past *$1", + "twenty five to *$2", + "twenty to *$2", + "quarter to *$2", + "ten to *$2", + "five to *$2" + ], + "text_scale":3.5 + }, + "es_ES":{ + "hours":[ + "doce", "una", "dos", "tres", "cuatro", "cinco", + "seis", "siete", "ocho", "nueve", "diez", "once", + "doce", "una", "dos", "tres", "cuatro", "cinco", + "seis", "siete", "ocho", "nueve", "diez", "once" + ], + "minutes":[ + "*$1 en punto", + "*$1 y cinco", + "*$1 y diez", + "*$1 y cuarto", + "*$1 y veinte", + "*$1 y veinti- cinco", + "*$1 y media", + "*$2 menos veinti- cinco", + "*$2 menos veinte", + "*$2 menos cuarto", + "*$2 menos diez", + "*$2 menos cinco" + ], + "text_scale":3.5 + }, + "fr_FR":{ + "hours":[ + "douze", "une", "deux", "trois", "quatre", "cinq", + "six", "sept", "huit", "neuf", "dix", "onze", + "douze", "une", "deux", "trois", "quatre", "cinq", + "six", "sept", "huit", "neuf", "dix", "onze" + ], + "minutes":[ + "*$1 heures", + "*$1 heures cinq", + "*$1 heures dix", + "*$1 heures et quart", + "*$1 heures vingt", + "*$1 heures vingt- cinq", + "*$1 heures et demie", + "*$2 moins vingt- cinq", + "*$2 heures moins vingt", + "*$2 moins le quart", + "*$2 heures moins dix", + "*$2 heures moins cinq" + ], + "text_scale":3.5 + }, + "no_NB":{ + "hours":[ + "tolv", "ett", "to", "tre", "fire", "fem", + "seks", "sju", "åtte", "ni", "ti", "elleve", + "tolv", "ett", "to", "tre", "fire", "fem", + "seks", "sju", "åtte", "ni", "ti", "elleve" + ], + "minutes":[ + "klokka er *$1", + "fem over *$1", + "ti over *$1", + "kvart over *$1", + "ti på halv *$2", + "fem på halv *$2", + "halv *$2", + "fem over halv *$2", + "ti over halv *$2", + "kvart på *$2", + "ti på *$2", + "fem på *$2" + ], + "text_scale":3.5 + }, + "sv_SE":{ + "hours":[ + "tolv", "ett", "två", "tre", "fyra", "fem", + "sex", "sju", "åtta", "nio", "tio", "elva", + "tolv", "ett", "två", "tre", "fyra", "fem", + "sex", "sju", "åtta", "nio", "tio", "elva" + ], + "minutes":[ + "*$1", + "fem över *$1", + "tio över *$1", + "kvart över *$1", + "tjugo över *$1", + "fem i halv *$2", + "halv *$2", + "fem över halv *$2", + "tjugo i *$2", + "kvart i *$2", + "tio i *$2", + "fem i *$2" + ], + "text_scale":3.5 + }, + "de_DE":{ + "hours":[ + "zwölf", "eins", "zwei", "drei", "vier", "fünf", + "sechs", "sieben", "acht", "neun", "zehn", "elf", + "zwölf", "eins", "zwei", "drei", "vier", "fünf", + "sechs", "sieben", "acht", "neun", "zehn", "elf" + ], + "minutes":[ + "*$1 uhr", + "fünf nach *$1", + "zehn nach *$1", + "viertel nach *$1", + "zwanzig nach *$1", + "fünf for halb *$2", + "halb *$2", + "fünf nach halb *$2", + "zwanzig vor *$2", + "viertel vor *$2", + "zehn vor *$2", + "fünf vor *$2" + ], + "text_scale":3.5 + } +} diff --git a/apps/fuzzyw/fuzzyw-dark.png b/apps/fuzzyw/fuzzyw-dark.png new file mode 100644 index 0000000000..88220e5c7f Binary files /dev/null and b/apps/fuzzyw/fuzzyw-dark.png differ diff --git a/apps/fuzzyw/fuzzyw-light.png b/apps/fuzzyw/fuzzyw-light.png new file mode 100644 index 0000000000..5383e08a49 Binary files /dev/null and b/apps/fuzzyw/fuzzyw-light.png differ diff --git a/apps/fuzzyw/fuzzyw.app.js b/apps/fuzzyw/fuzzyw.app.js new file mode 100644 index 0000000000..07b5c40682 --- /dev/null +++ b/apps/fuzzyw/fuzzyw.app.js @@ -0,0 +1,75 @@ +// adapted from https://github.com/hallettj/Fuzzy-Text-International/ +const fuzzy_strings = require("Storage").readJSON("fuzzy_strings.json"); + +const SETTINGS_FILE = "fuzzyw.settings.json"; +let settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'language': 'System', 'alignment':'Centre'}; + +if (settings.language == 'System') { + settings.language = require('locale').name; +} + +let fuzzy_string = fuzzy_strings[settings.language]; + +let timeout = 2.5*60; +let drawTimeout; + +function queueDraw(seconds) { + let millisecs = seconds * 1000; + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, millisecs - (Date.now() % millisecs)); +} + +const h = g.getHeight(); +const w = g.getWidth(); +let align_mode = 0; +let align_pos = w/2; +if (settings.alignment =='Left') { + align_mode = -1; + align_pos = 0; +} else if (settings.alignment == 'Right') { + align_mode = 1; + align_pos = w; +} + +function getTimeString(date) { + let segment = Math.round((date.getMinutes()*60 + date.getSeconds() + 1)/300); + let hour = date.getHours() + Math.floor(segment/12); + f_string = fuzzy_string.minutes[segment % 12]; + if (f_string.includes('$1')) { + f_string = f_string.replace('$1', fuzzy_string.hours[(hour) % 24]); + } else { + f_string = f_string.replace('$2', fuzzy_string.hours[(hour + 1) % 24]); + } + return f_string; +} + +function draw() { + let time_string = getTimeString(new Date()).replace('*', ''); + // print(time_string); + g.setFont('Vector', (h-24*2)/fuzzy_string.text_scale); + g.setFontAlign(align_mode, 0); + g.clearRect(0, 24, w, h-24); + g.setColor(g.theme.fg); + g.drawString(g.wrapString(time_string, w).join("\n"), align_pos, h/2); + queueDraw(timeout); +} + +g.clear(); +draw(); + +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +Bangle.setUI('clock'); +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/fuzzyw/fuzzyw.icon.js b/apps/fuzzyw/fuzzyw.icon.js new file mode 100644 index 0000000000..acc7e2fcff --- /dev/null +++ b/apps/fuzzyw/fuzzyw.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgP/ABX8oYFD+AFE8AFE8IXE8YFKwFCj08h4FBocenEHCIPDjk4CoIFBhlwAoeMuIFEuBSBAoOI+AFD4HxGoQFB+AFD4P4uYFC8P4gYFD/w7BAFEfApfEj+B/Ecg/Ah8A+EMg/Dw0YseHj/Dw/8sfHAoPH/lhDoIFBwFwj4FB40AvkPAoU8v4dCAoIdDw04FIMP4EOgFwh47Bj8EvEfw/DJwgFXABY")) diff --git a/apps/fuzzyw/fuzzyw.png b/apps/fuzzyw/fuzzyw.png new file mode 100644 index 0000000000..afd0b0f766 Binary files /dev/null and b/apps/fuzzyw/fuzzyw.png differ diff --git a/apps/fuzzyw/fuzzyw.settings.js b/apps/fuzzyw/fuzzyw.settings.js new file mode 100644 index 0000000000..00219accfc --- /dev/null +++ b/apps/fuzzyw/fuzzyw.settings.js @@ -0,0 +1,46 @@ +(function(back) { + const SETTINGS_FILE = "fuzzyw.settings.json"; + + var align_options = ['Left','Centre','Right']; + var language_options = ['System', 'en_GB', 'en_US', 'es_ES', 'fr_FR', 'no_NO', 'sv_SE', 'de_DE']; + + // initialize with default settings... + let s = {'language': 'System', 'alignment': 'Centre'}; + + // ...and overwrite them with any saved values + // This way saved values are preserved if a new version adds more settings + const storage = require('Storage') + let settings = storage.readJSON(SETTINGS_FILE, 1) || s; + const saved = settings || {} + for (const key in saved) { + s[key] = saved[key] + } + + function save() { + settings = s + storage.write(SETTINGS_FILE, settings) + } + + E.showMenu({ + '': { 'title': 'Fuzzy Text Clock' }, + '< Back': back, + 'Language': { + value: 0 | language_options.indexOf(s.language), + min: 0, max: language_options.length - 1, + format: v => language_options[v], + onchange: v => { + s.language = language_options[v]; + save(); + } + }, + 'Alignment': { + value: 0 | align_options.indexOf(s.alignment), + min: 0, max: align_options.length - 1, + format: v => align_options[v], + onchange: v => { + s.alignment = align_options[v]; + save(); + } + }, + }); +}) diff --git a/apps/fuzzyw/metadata.json b/apps/fuzzyw/metadata.json new file mode 100644 index 0000000000..ebd20e49fb --- /dev/null +++ b/apps/fuzzyw/metadata.json @@ -0,0 +1,20 @@ +{ + "id":"fuzzyw", + "name":"Fuzzy Text Clock", + "shortName": "Fuzzy Text", + "version": "0.01", + "description": "An imprecise clock for when you're not in a rush", + "readme": "README.md", + "icon":"fuzzyw.png", + "screenshots": [{"url":"fuzzyw-light.png"},{"url":"fuzzyw-dark.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS", "BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"fuzzyw.app.js","url":"fuzzyw.app.js"}, + {"name":"fuzzyw.settings.js","url":"fuzzyw.settings.js"}, + {"name":"fuzzyw.img","url":"fuzzyw.icon.js","evaluate":true}, + {"name":"fuzzy_strings.json","url":"fuzzy_strings.json"} + ] +}