From fa090ff8da6108f9f1e879fe27ae985620dee26a Mon Sep 17 00:00:00 2001 From: Andrei Georgian CRISTINESCU Date: Thu, 14 Nov 2019 21:45:04 +0200 Subject: [PATCH] [app] promodoro --- apps.json | 13 ++ apps/apps.json | 519 ++++++++++++++++++++++++++++++++++++++++++++ apps/promodoro.js | 240 ++++++++++++++++++++ apps/promodoro.json | 5 + apps/promodoro.png | Bin 0 -> 2122 bytes 5 files changed, 777 insertions(+) create mode 100644 apps/apps.json create mode 100644 apps/promodoro.js create mode 100644 apps/promodoro.json create mode 100644 apps/promodoro.png diff --git a/apps.json b/apps.json index f2cb1a27a..978f28060 100644 --- a/apps.json +++ b/apps.json @@ -502,5 +502,18 @@ {"name": "-gpsinfo","url": "gps-info.js"}, {"name": "*gpsinfo","url": "gps-info-icon.js","evaluate": true} ] + }, + { + "id": "promodoro", + "name":"Promodoro", + "icon":"promodoro.png", + "description": "A simple promodoro timer.", + "tags": "promodoro", + "type": "app", + "storage": [ + {"name": "+promodoro","url": "promodoro.json"}, + {"name": "-promodoro","url": "promodoro.js"}, + {"name": "*promodoro","url": "promodoro.js","evaluate": true} + ] } ] diff --git a/apps/apps.json b/apps/apps.json new file mode 100644 index 000000000..978f28060 --- /dev/null +++ b/apps/apps.json @@ -0,0 +1,519 @@ +[ + { "id": "boot", + "name": "Bootloader", + "icon": "bootloader.png", + "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", + "tags": "tool,system", + "storage": [ + {"name":".bootcde","url":"bootloader.js"} + ], + "sortorder" : -10 + }, + { "id": "mclock", + "name": "Morphing Clock", + "icon": "clock-morphing.png", + "description": "7 segment clock that morphs between minutes and hours", + "tags": "clock", + "type":"clock", + "storage": [ + {"name":"+mclock","url":"clock-morphing.json"}, + {"name":"-mclock","url":"clock-morphing.js"}, + {"name":"*mclock","url":"clock-morphing-icon.js","evaluate":true} + ], + "sortorder" : -9 + }, + { "id": "setting", + "name": "Settings", + "icon": "settings.png", + "description": "A menu for setting up Bangle.js - by default this disables Bluetooth unless you enable 'BLE' AND 'Dev'", + "tags": "tool,system", + "storage": [ + {"name":"+setting","url":"settings.json"}, + {"name":"-setting","url":"settings.js"}, + {"name":"=setting","url":"settings-init.js"}, + {"name":"@setting","url":"settings-default.json","evaluate":true}, + {"name":"*setting","url":"settings-icon.js","evaluate":true} + ], + "sortorder" : -2 + }, + { "id": "wclock", + "name": "Word Clock", + "icon": "clock-word.png", + "description": "Display Time as Text", + "tags": "clock", + "type":"clock", + "storage": [ + {"name":"+wclock","url":"clock-word.json"}, + {"name":"-wclock","url":"clock-word.js"}, + {"name":"*wclock","url":"clock-word-icon.js","evaluate":true} + ] + }, + { "id": "aclock", + "name": "Analog Clock", + "icon": "clock-analog.png", + "description": "An Analog Clock", + "tags": "clock", + "type":"clock", + "storage": [ + {"name":"+aclock","url":"clock-analog.json"}, + {"name":"-aclock","url":"clock-analog.js"}, + {"name":"*aclock","url":"clock-analog-icon.js","evaluate":true} + ] + }, + { "id": "trex", + "name": "T-Rex", + "icon": "trex.png", + "description": "T-Rex game in the style of Chrome's offline game", + "tags": "game", + "storage": [ + {"name":"+trex","url":"trex.json"}, + {"name":"-trex","url":"trex.js"}, + {"name":"*trex","url":"trex-icon.js","evaluate":true} + ] + }, + { "id": "astroid", + "name": "Asteroids!", + "icon": "asteroids.png", + "description": "Retro asteroids game", + "tags": "game", + "storage": [ + {"name":"+astroid","url":"asteroids.json"}, + {"name":"-astroid","url":"asteroids.js"}, + {"name":"*astroid","url":"asteroids-icon.js","evaluate":true} + ] + }, + { "id": "clickms", + "name": "Click Master", + "icon": "click-master.png", + "description": "Get several friends to start the game, then compete to see who can press BTN1 the most!", + "tags": "game", + "storage": [ + {"name":"+clickms","url":"click-master.json"}, + {"name":"-clickms","url":"click-master.js"}, + {"name":"*clickms","url":"click-master-icon.js","evaluate":true} + ] + }, + { "id": "horsey", + "name": "Horse Race!", + "icon": "horse-race.png", + "description": "Get several friends to start the game, then compete to see who can press BTN1 the most!", + "tags": "game", + "storage": [ + {"name":"+horsey","url":"horse-race.json"}, + {"name":"-horsey","url":"horse-race.js"}, + {"name":"*horsey","url":"horse-race-icon.js","evaluate":true} + ] + }, + { "id": "compass", + "name": "Compass", + "icon": "compass.png", + "description": "Simple compass that points North", + "tags": "tool,outdoors", + "storage": [ + {"name":"+compass","url":"compass.json"}, + {"name":"-compass","url":"compass.js"}, + {"name":"*compass","url":"compass-icon.js","evaluate":true} + ] + }, + { "id": "gpstime", + "name": "GPS Time", + "icon": "gpstime.png", + "description": "Update the Bangle.js's clock based on the time from the GPS receiver", + "tags": "tool", + "storage": [ + {"name":"+gpstime","url":"gpstime.json"}, + {"name":"-gpstime","url":"gpstime.js"}, + {"name":"*gpstime","url":"gpstime-icon.js","evaluate":true} + ] + }, + { "id": "openloc", + "name": "Open Location / Plus Codes", + "icon": "openlocation.png", + "description": "Convert your current GPS location to a series of characters", + "tags": "tool,outdoors", + "storage": [ + {"name":"+openloc","url":"openlocation.json"}, + {"name":"-openloc","url":"openlocation.js","evaluate":true} + ] + }, + { "id": "speedo", + "name": "Speedo", + "icon": "speedo.png", + "description": "Show the current speed according to the GPS", + "tags": "tool,outdoors", + "storage": [ + {"name":"+speedo","url":"speedo.json"}, + {"name":"-speedo","url":"speedo.js"}, + {"name":"*speedo","url":"speedo-icon.js","evaluate":true} + ] + }, + { "id": "slevel", + "name": "Spirit Level", + "icon": "spiritlevel.png", + "description": "Show the current angle of the watch, so you can use it to make sure something is absolutely flat", + "tags": "tool", + "storage": [ + {"name":"+slevel","url":"spiritlevel.json"}, + {"name":"-slevel","url":"spiritlevel.js"}, + {"name":"*slevel","url":"spiritlevel-icon.js","evaluate":true} + ] + }, + { "id": "files", + "name": "App Manager", + "icon": "files.png", + "description": "Show currently installed apps, free space, and allow their deletion from the watch", + "tags": "tool,system", + "storage": [ + {"name":"+files","url":"files.json"}, + {"name":"-files","url":"files.js"}, + {"name":"*files","url":"files-icon.js","evaluate":true} + ] + }, + { "id": "sbat", + "name": "Battery Level Widget", + "icon": "widget-battery.png", + "description": "Show the current battery level and charging status in the top right of the clock", + "tags": "widget,battery", + "type":"widget", + "storage": [ + {"name":"+sbat","url":"widget-battery.json"}, + {"name":"=sbat","url":"widget-battery.js"} + ] + }, + { "id": "sbt", + "name": "Bluetooth Widget", + "icon": "widget-bluetooth.png", + "description": "Show the current Bluetooth connection status in the top right of the clock", + "tags": "widget,bluetooth", + "type":"widget", + "storage": [ + {"name":"+sbt","url":"widget-bluetooth.json"}, + {"name":"=sbt","url":"widget-bluetooth.js"} + ] + }, + { "id": "hrm", + "name": "Heart Rate Monitor", + "icon": "heartrate.png", + "description": "Measure your current heart rate", + "tags": "health", + "storage": [ + {"name":"+hrm","url":"heartrate.json"}, + {"name":"-hrm","url":"heartrate.js"}, + {"name":"*hrm","url":"heartrate-icon.js","evaluate":true} + ] + }, + { "id": "swatch", + "name": "Stopwatch", + "icon": "stopwatch.png", + "description": "Simple stopwatch with Lap Time recording", + "tags": "health", + "storage": [ + {"name":"+swatch","url":"stopwatch.json"}, + {"name":"-swatch","url":"stopwatch.js"}, + {"name":"*swatch","url":"stopwatch-icon.js","evaluate":true} + ] + }, + { "id": "hidmsic", + "name": "Bluetooth Music Controls", + "icon": "hid-music.png", + "description": "Enable HID in settings, pair with your phone, then use this app to control music from your watch!", + "tags": "bluetooth", + "storage": [ + {"name":"+hidmsic","url":"hid-music.json"}, + {"name":"-hidmsic","url":"hid-music.js"}, + {"name":"*hidmsic","url":"hid-music-icon.js","evaluate":true} + ] + }, + { "id": "hidkbd", + "name": "Bluetooth Keyboard", + "icon": "hid-keyboard.png", + "description": "Enable HID in settings, pair with your phone/PC, then use this app to control other apps", + "tags": "bluetooth", + "storage": [ + {"name":"+hidkbd","url":"hid-keyboard.json"}, + {"name":"-hidkbd","url":"hid-keyboard.min.js"}, + {"name":"*hidkbd","url":"hid-keyboard-icon.js","evaluate":true} + ] + }, + { "id": "hidbkbd", + "name": "Binary Bluetooth Keyboard", + "icon": "hid-binary-keyboard.png", + "description": "Enable HID in settings, pair with your phone/PC, then type messages using the onscreen keyboard by tapping repeatedly on the key you want", + "tags": "bluetooth", + "storage": [ + {"name":"+hidbkbd","url":"hid-binary-keyboard.json"}, + {"name":"-hidbkbd","url":"hid-binary-keyboard.js"}, + {"name":"*hidbkbd","url":"hid-binary-keyboard-icon.js","evaluate":true} + ] + }, + { "id": "animals", + "name": "Animals Game", + "icon": "animals.png", + "description": "Simple toddler's game - displays a different number of animals each time the screen is pressed", + "tags": "game", + "storage": [ + {"name":"+animals","url":"animals.json"}, + {"name":"-animals","url":"animals.js"}, + {"name":"*animals","url":"animals-icon.js","evaluate":true}, + {"name":"*snake","url":"animals-snake.js","evaluate":true}, + {"name":"*duck","url":"animals-duck.js","evaluate":true}, + {"name":"*swan","url":"animals-swan.js","evaluate":true}, + {"name":"*fox","url":"animals-fox.js","evaluate":true}, + {"name":"*camel","url":"animals-camel.js","evaluate":true}, + {"name":"*pig","url":"animals-pig.js","evaluate":true}, + {"name":"*sheep","url":"animals-sheep.js","evaluate":true}, + {"name":"*mouse","url":"animals-mouse.js","evaluate":true} + ], + "sortorder" : 1 + }, + { "id": "qrcode", + "name": "Custom QR Code", + "icon": "qrcode.png", + "description": "Use this to upload a customised QR code to Bangle.js", + "tags": "", + "custom": "qrcode.html", + "storage": [ + {"name":"-qrcode"}, + {"name":"+qrcode"}, + {"name":"=qrcode"} + ] + }, + { "id": "beer", + "name": "Beer Compass", + "icon": "beercompass.png", + "description": "Uploads all the pubs in an area onto your watch, so it can always point you at the nearest one", + "tags": "", + "custom": "beercompass.html", + "storage": [ + {"name":"-beer"}, + {"name":"+beer"}, + {"name":"=beer"} + ] + }, + { "id": "route", + "name": "Route Viewer", + "icon": "route.png", + "description": "Upload a KML file of a route, and have your watch display a map with how far around it you are", + "tags": "", + "custom": "route.html", + "storage": [ + {"name":"-route"}, + {"name":"+route"}, + {"name":"=route"} + ] + }, + + { + "id": "start", + "name": "NCEU Startup", + "icon": "start.png", + "description": "NodeConfEU 2019 Startup Sequence", + "tags": "start", + "storage": [ + {"name":"+start","url":"start.json"}, + {"name":"-start","url":"start.js"}, + {"name":"*start","url":"start-icon.js","evaluate":true}, + {"name":"*bangle","url":"start-bangle.js","evaluate":true}, + {"name":"*nceu","url":"start-nceu.js","evaluate":true}, + {"name":"*nfr","url":"start-nfr.js","evaluate":true}, + {"name":"*nodew","url":"start-nodew.js","evaluate":true}, + {"name":"*tf","url":"start-tf.js","evaluate":true} + ], + "sortorder" : -1 + }, + { "id": "funrun5", + "name": "NCEU 5K Fun Run", + "icon": "nceu-funrun.png", + "description": "Display a map of the NodeConf EU 2019 5K Fun Run route and your location on it", + "tags": "health", + "storage": [ + {"name":"+funrun5","url":"nceu-funrun.json"}, + {"name":"-funrun5","url":"nceu-funrun.js"}, + {"name":"*funrun5","url":"nceu-funrun-icon.js","evaluate":true} + ], + "sortorder" : -1 + }, + { "id": "nceuwid", + "name": "NCEU Logo Widget", + "icon": "nceu-widget.png", + "description": "Show the NodeConf EU logo in the top left", + "tags": "widget", + "type":"widget", + "storage": [ + {"name":"+nceuwid","url":"nceu-widget.json"}, + {"name":"=nceuwid","url":"nceu-widget.js"} + ], + "sortorder" : -1 + }, + { "id": "sclock", + "name": "Simple Clock", + "icon": "clock-simple.png", + "description": "Simple Digital Clock", + "tags": "sclock", + "type":"clock", + "storage": [ + {"name":"+sclock","url":"clock-simple.json"}, + {"name":"-sclock","url":"clock-simple.js"}, + {"name":"*sclock","url":"clock-simple-icon.js","evaluate":true} + ] + }, + { "id": "gesture", + "name": "Gesture Test", + "icon": "gesture.png", + "description": "BETA! Uploads a basic Tensorflow Gesture model, and then outputs each gesture as a message", + "tags": "gesture,ai", + "type":"app", + "storage": [ + {"name":"+gesture","url":"gesture.json"}, + {"name":"-gesture","url":"gesture.js"}, + {"name":".tfnames","url":"gesture-tfnames.js","evaluate":true}, + {"name":".tfmodel","url":"gesture-tfmodel.js","evaluate":true}, + {"name":"*gesture","url":"gesture-icon.js","evaluate":true} + ] + }, + { "id": "pparrot", + "name": "Party Parrot", + "icon": "party-parrot.png", + "description": "Party with a parrot on your wrist", + "tags": "party,parrot,lol", + "type":"app", + "storage": [ + {"name":"+pparrot","url":"party-parrot.json"}, + {"name":"-pparrot","url":"party-parrot.js"}, + {"name":"*pparrot","url":"party-parrot-icon.js","evaluate":true} + ] + }, + { "id": "hrings", + "name": "Hypno Rings", + "icon": "hypno-rings.png", + "description": "Experiment with trippy rings, press buttons for change", + "tags": "rings,hypnosis,psychadelic", + "type":"app", + "storage": [ + {"name":"+hrings","url":"hypno-rings.json"}, + {"name":"-hrings","url":"hypno-rings.js"}, + {"name":"*hrings","url":"hypno-rings-icon.js","evaluate":true} + ] + }, + { "id": "morse", + "name": "Morse Code", + "icon": "morse-code.png", + "description": "Learn morse code by hearing/seeing/feeling the code. Tap to toggle buzz!", + "tags": "morse,sound,visual,input", + "type":"app", + "storage": [ + {"name":"+morse","url":"morse-code.json"}, + {"name":"-morse","url":"morse-code.js"}, + {"name":"*morse","url":"morse-code-icon.js","evaluate":true} + ] + }, + { + "id": "blescan", + "name": "BLE Scanner", + "icon": "blescan.png", + "description": "Scan for advertising BLE devices", + "tags" : "bluetooth", + "storage" : [ + {"name":"+blescan","url":"blescan.json"}, + {"name":"-blescan","url":"blescan.js"}, + {"name":"*blescan","url":"blescan-icon.js", "evaluate":true} + ] + }, + { "id": "mmonday", + "name": "Manic Monday Tone", + "icon": "manic-monday-icon.png", + "description": "The Bangles make a comeback", + "tags": "sound", + "storage": [ + {"name":"+mmonday","url":"manic-monday.json"}, + {"name":"-mmonday","url":"manic-monday.js"}, + {"name":"*mmonday","url":"manic-monday-icon.js","evaluate":true} + ] + }, + { "id": "jbells", + "name": "Jingle Bells", + "icon": "jbells.png", + "description": "Play Jingle Bells", + "tags": "sound", + "type":"app", + "storage": [ + {"name":"+jbells","url":"jbells.json"}, + {"name":"-jbells","url":"jbells.js"}, + {"name":"*jbells","url":"jbells-icon.js","evaluate":true} + ] + }, + { "id": "scolor", + "name": "Show Color", + "icon": "show-color.png", + "description": "Display all available Colors and Names", + "tags": "tool", + "type":"app", + "storage": [ + {"name":"+scolor","url":"show-color.json"}, + {"name":"-scolor","url":"show-color.js"}, + {"name":"*scolor","url":"show-color-icon.js","evaluate":true} + ] + }, + { "id": "miclock", + "name": "Mixed Clock", + "icon": "clock-mixed.png", + "description": "A mix of analog and digital Clock", + "tags": "clock", + "type":"clock", + "storage": [ + {"name":"+miclock","url":"clock-mixed.json"}, + {"name":"-miclock","url":"clock-mixed.js"}, + {"name":"*miclock","url":"clock-mixed-icon.js","evaluate":true} + ] + }, + { "id": "bclock", + "name": "Binary Clock", + "icon": "clock-binary.png", + "description": "A simple binary clock watch face", + "tags": "clock", + "type":"clock", + "storage": [ + {"name":"+bclock","url":"clock-binary.json"}, + {"name":"-bclock","url":"clock-binary.js"}, + {"name":"*bclock","url":"clock-binary-icon.js","evaluate":true} + ] + }, + { "id": "clotris", + "name": "Clock-Tris", + "icon": "clock-tris.png", + "description": "A fully functional clone of a classic game of falling blocks", + "tags": "game", + "storage": [ + {"name":"+clotris","url":"clock-tris.json"}, + {"name":"-clotris","url":"clock-tris.js"}, + {"name":"*clotris","url":"clock-tris-icon.js","evaluate":true}, + {"name":".trishig","url":"clock-tris-high"} + ] + }, + { + "id": "gpsinfo", + "name": "GPS Info", + "icon": "gps-info.png", + "description": "An application that displays information about altitude, lat/lon, satellites and time", + "tags": "gps", + "type": "app", + "storage": [ + {"name": "+gpsinfo","url": "gps-info.json"}, + {"name": "-gpsinfo","url": "gps-info.js"}, + {"name": "*gpsinfo","url": "gps-info-icon.js","evaluate": true} + ] + }, + { + "id": "promodoro", + "name":"Promodoro", + "icon":"promodoro.png", + "description": "A simple promodoro timer.", + "tags": "promodoro", + "type": "app", + "storage": [ + {"name": "+promodoro","url": "promodoro.json"}, + {"name": "-promodoro","url": "promodoro.js"}, + {"name": "*promodoro","url": "promodoro.js","evaluate": true} + ] + } +] diff --git a/apps/promodoro.js b/apps/promodoro.js new file mode 100644 index 000000000..00e8916a2 --- /dev/null +++ b/apps/promodoro.js @@ -0,0 +1,240 @@ +const DEFAULT_TIME = 1500; // 25m +const TIME_BREAK = 300; +const STATES = { + INIT: 1, + STARTED: 2, + DONE: 3, + BREAK: 4 +}; +var counterInterval; + +class State { + constructor (state) { + this.state = state; + this.next = null; + } + + goNext () { + if (this.next) { + this.next.run(); + } + } + + setNext (next) { + this.next = next; + } + + setButtons () { + setWatch(() => { console.log('BTN1') }, BTN1, { repeat: true }); + setWatch(() => { console.log('BTN2') }, BTN2, { repeat: true }); + setWatch(() => { console.log('BTN3') }, BTN3, { repeat: true }); + } + clear () { + clearWatch(); + g.clear(); + g.setFontAlign(0, 0); + } + + draw () { + g.clear(); + } + + init () { } + + go (nextState) { + if (nextState) { + this.next = nextState; + } + + this.clear(); + this.init(); + this.setButtons(); + this.draw(); + } +} + +class InitState extends State { + constructor () { + super(STATES.INIT); + + this.timeCounter = DEFAULT_TIME; + } + + setButtons () { + setWatch(() => { + if (this.timeCounter + 300 > 3599) { + this.timeCounter = 3599; + } else { + this.timeCounter += 300; + } + + this.draw(); + + }, BTN1, { repeat: true }); + + setWatch(() => { + if (this.timeCounter - 300 > 0) { + this.timeCounter -= 300; + this.draw(); + } + }, BTN3, { repeat: true }); + + setWatch(() => { + const startedState = new StartedState(this.timeCounter); + + this.setNext(startedState); + this.next.go(); + }, BTN2, { repeat: true }); + } + + draw () { + g.clear(); + g.setFontAlign(0, 0); // center font + g.setFont("Vector", 50); // vector font, 80px + drawCounter(this.timeCounter); + } +} + +class StartedState extends State { + constructor (timeCounter) { + super(STATES.STARTED); + + this.timeCounter = timeCounter || DEFAULT_TIME; + } + + draw () { + drawCounter(this.timeCounter, 120, 120); + } + + init () { + function countDown () { + this.timeCounter--; + + // Out of time + if (this.timeCounter <= 0) { + clearInterval(counterInterval); + counterInterval = undefined; + this.next.go(); + return; + } + + this.draw(); + } + + const doneState = new DoneState(); + this.setNext(doneState); + counterInterval = setInterval(countDown.bind(this), 1000); + } +} + +class BreakState extends State { + constructor () { + super(STATES.BREAK); + } + + draw () { + g.setFontAlign(0, 0); + } + + init () { + const startedState = new StartedState(TIME_BREAK); + + this.setNext(startedState); + this.next.go(); + } +} +class DoneState extends State { + constructor () { + super(STATES.DONE); + } + + setButtons () { + setWatch(() => { + const initState = new InitState(); + clearTimeout(this.timeout); + initState.go(); + }, BTN1, { repeat: true }); + + setWatch(() => { + const breakState = new BreakState(); + clearTimeout(this.timeout); + breakState.go(); + }, BTN3, { repeat: true }); + + setWatch(() => { + }, BTN2, { repeat: true }); + } + + draw () { + g.clear(); + g.setFont("6x8", 2); + g.setFontAlign(0, 0, 3); + g.drawString("AGAIN", 230, 50); + g.drawString("BREAK", 230, 190); + g.setFont("Vector", 45); + g.setFontAlign(-1, -1); + + g.drawString('You \\nare \\na \\nhero!', 50, 40); + } + + init () { + + function buzz () { + Bangle.buzz(); + Bangle.beep(200, 4000) + .then(() => new Promise(resolve => setTimeout(resolve, 50))) + .then(() => Bangle.beep(200, 3000)) + .then(() => new Promise(resolve => setTimeout(resolve, 200))) + .then(() => Bangle.beep(200, 3000)) + .then(() => new Promise(resolve => setTimeout(resolve, 300))) + .then(() => { + Bangle.beep(200, 3000); + Bangle.buzz() + }); + } + + buzz(); + // again, 10 secs later + this.timeout = setTimeout(buzz.bind(this), 10000); + } +} + +function drawCounter (currentValue, x, y) { + if (currentValue < 0) { + return; + } + + x = x || 120; + y = y || 120; + + let minutes = 0; + let seconds = 0; + + if (currentValue >= 60) { + minutes = Math.floor(currentValue / 60); + seconds = currentValue % 60; + } else { + seconds = currentValue; + } + + let minutesString = '' + minutes; + let secondsString = '' + seconds; + + if (minutes < 10) { + minutesString = '0' + minutes; + } + + if (seconds < 10) { + secondsString = '0' + seconds; + } + + g.clear(); + g.drawString(minutesString + ':' + secondsString, x, y); +} + +function init () { + const initState = new InitState(); + initState.go(); +} + +init(); \ No newline at end of file diff --git a/apps/promodoro.json b/apps/promodoro.json new file mode 100644 index 000000000..03a0518ac --- /dev/null +++ b/apps/promodoro.json @@ -0,0 +1,5 @@ +{ + "name":"Promodoro","type":"app", + "icon":"*promodoro", + "src":"-promodoro" +} \ No newline at end of file diff --git a/apps/promodoro.png b/apps/promodoro.png new file mode 100644 index 0000000000000000000000000000000000000000..4ea72f270d5633755b90ede38c67cdc7e121e4c5 GIT binary patch literal 2122 zcmV-Q2(|Z#P)vh7l(-y(Eff0j)pz+4-E-b}m z^l1UU0sJkAf_|W6kOHNyIGvr_*sva<9~!I?Jkb@W=RYLoDrwjOgGsO==$POP?*AJ? zSCc6Ck74~?@$_KUUg&FvZ4|l$oLEWlunY$TCD7ZzgDoC!VDxiJA(A+Jtq2t^n77$H1Ng ztpSQwJ=k^Nu))U!-!rTvwB5Ja32X+Mq1>xv*ma?1pdWZDjMKW-$&wBvQB$WupU?w9 zFK{%B)8p>9U^DQr!3MA%P`fHo9R~uS18maZgis$YDz)Squsw{^_eP_jRZs}y^!ls0GZ8n#dH z^Dv&C9gUiH2t91@Tj0&EIK5Gq1D=PW!B4^ZgbI06{(jkDXIGq_NTQ$!+zsqv6mw|`o zg7=_i!6pRb9(?3y>1l7U;M-m`?1t|@Db$~vvm$VpVeLZi14p{zbP%jZ@Pe>I8F=5L z7kCBit4S2J)N}xs0S*~N`OwS>(*S4}a2{A|a0q9B%voSo@OL3!an)hi+dy1y0nLI< zz<#iONffk!t^)r6a&)omZ#U?mVcYUx%GkVTuNyo#e_>b(y6hIB?FCI4gu>net*aO+ z_dEsm3TQXbZ5V>nhMtgfcpK1S6=Kc^{lT#PUGemA5(OQ=m%%=7XajJkUrPWb;B!J} z!Z@vpz+(%5pK&v-=Yg*SyFn%3KEY83KAX{Og;-+F$pkEL;035xu43;2ju8I%e1*p{Kx30%;{fu|?QEU?W&o)OuZ<%HtroAJ{S) zHBlUmnhLmzx*?nP+=6WZwj25rKv$ezPom&u;39A@ZlTQp4fl|t$ z7sA#9?{vk}Q^2c96ubt6-gXNx0XiARX(@?7^hdlI8A_G`D+o;W#C$cLIC=B_G=Oao4_uF@$@C&Qog5K&^!sYRXOl; z^i4u*in!v_n-5Vw3g$xlUEtm%3SI!-4HwvTX7vNu0{p^Y3>c7Ggv^~BgTsE`^g3`B zYz7nn_W@r5HoLk3^C=|2)`0FRdZRs6b2nda1^WColtjU4;NON_6ABDIfvX4Wb1&Td7KEh zU=eoQupi?#Y6I?>x)-NMeY#+Ema3{`ad=C+2iy&Mb~FlBh4J(?p&tRC4ddxCzX9Cr zdRVS^S?nM!CY?iI5<=5loaB~kDJ*y?-)mWsqeSz<9ANPyo19{{_7HH!>iw-`23 ztbn!!n$%8kR;TVIH2D7f#8~_FrXC%bEXr*Ks>3qRiHfdy+R}P8e``Hoj?P2@m)^>Q z=kK+|a&udc-iWh}=^8G84QB?KcQH?|%tC#7cG*Xu1OF<3&Sf5Xugdq#*SuaSBK7H& z;5bv{C411A^+qI?ijNyRSpOe z!E-{FZ?!BWGy!&$IVTv^`!+90)U*xsODad|Rbn|4_2?@gCEPar#knUU3x1_Ecnxke?*w|O7WgF{$T@O9*mppEU@fBZwzZU=8L*tOCG;6XX9Y)r-z`L&ywqE?`h_kT z{6+3`y0eiS-e8eBf;;^lSqi+f(V+N`4^JERsL)!2wrrqfQmACuh)@h13*&TfsSbHr z7Dbb&33K0vzX4cTFmT@BoX|VKP#C8(jbiywY1*Giw*sADcN(k{ zx(Dbs_q^zhq=rp_62U)#L32kf6N1Tls8UP*2Ty*k3rtPI!T