diff --git a/apps/messages_light/ChangeLog b/apps/messages_light/ChangeLog new file mode 100644 index 000000000..328e2a120 --- /dev/null +++ b/apps/messages_light/ChangeLog @@ -0,0 +1,7 @@ +1.0: New App! +1.1: fix app opening when a remove notification arrives +1.2: message_light overrides require() by sending requests to "message" to a proxy library which overrides pushMessage + settings now points to message settings + implemented use of the "messageicons" library + removed lib no longer used +1.3: icon changed \ No newline at end of file diff --git a/apps/messages_light/README.md b/apps/messages_light/README.md new file mode 100644 index 000000000..00fe39bd0 --- /dev/null +++ b/apps/messages_light/README.md @@ -0,0 +1,11 @@ +# Messages app + +This app handles the display of messages and message notifications. + +It is a GUI replacement for the `messages` apps. + + +## Creator + +Rarder44 + diff --git a/apps/messages_light/app-icon.js b/apps/messages_light/app-icon.js new file mode 100644 index 000000000..7d1da35c9 --- /dev/null +++ b/apps/messages_light/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA/4ACBIMQwhL/ABMBqoAEoALDioLFqgLDBQoABERIkEBZcFBY9QBYVe1QAB1YLGrSlC/YLGrYHCr4Lrr9drpLC1oLEAAN5rxKB/ILHEYV5EY4LIHYoLorRaBqoPCBYlfUoXrBYwGBrdeDIILIvXVBZFa1I+CBY/5BZIHBBwOq1ILGrXVvf//oLGq+trLLFBYVVvQxCBY9XJIQLCgILDHoVVoALHAAQLCgALHBQUAioKFqgLDEgwiDAH4AGA")) \ No newline at end of file diff --git a/apps/messages_light/app-icon.png b/apps/messages_light/app-icon.png new file mode 100644 index 000000000..c9b4b62ac Binary files /dev/null and b/apps/messages_light/app-icon.png differ diff --git a/apps/messages_light/app.png b/apps/messages_light/app.png new file mode 100644 index 000000000..1f738504d Binary files /dev/null and b/apps/messages_light/app.png differ diff --git a/apps/messages_light/full-size-app.png b/apps/messages_light/full-size-app.png new file mode 100644 index 000000000..2df7915ed Binary files /dev/null and b/apps/messages_light/full-size-app.png differ diff --git a/apps/messages_light/messages_light.app.js b/apps/messages_light/messages_light.app.js new file mode 100644 index 000000000..5d5363d38 --- /dev/null +++ b/apps/messages_light/messages_light.app.js @@ -0,0 +1,496 @@ +/* MESSAGES is a list of: + {id:int, + src, + title, + subject, + body, + sender, + tel:string, + new:true // not read yet + } +*/ + +let LOG=function(){ + //print.apply(null, arguments); +} + + + + +let settings= (()=>{ + let tmp={}; + tmp.NewEventFileName="messages_light.NewEvent.json"; + + tmp.fontSmall = "6x8"; + tmp.fontMedium = g.getFonts().includes("Vector")?"Vector:16":"6x8:2"; + tmp.fontBig = g.getFonts().includes("12x20")?"12x20":"6x8:2"; + tmp.fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4"; + + + tmp.colHeadBg = g.theme.dark ? "#141":"#4f4"; + tmp.colBg = g.theme.dark ? "#000":"#fff"; + tmp.colLock = g.theme.dark ? "#ff0000":"#ff0000"; + + tmp.quiet=((require('Storage').readJSON('setting.json', 1) || {}).quiet) + + return tmp; +})(); +let EventQueue=[]; //in posizione 0, c'è quello attualmente visualizzato +let callInProgress=false; + + + + +//TODO: RICORDARSI DI FARE IL DELETE +var manageEvent = function(event) { + + event.new=true; + + + LOG("manageEvent"); + if( event.id=="call") + { + showCall(event); + return; + } + switch(event.t) + { + case "add": + EventQueue.unshift(event); + + if(!callInProgress) + showMessage(event); + break; + + case "modify": + //cerco l'evento nella lista, se lo trovo, lo modifico, altrimenti lo pusho + let find=false; + EventQueue.forEach(element => { + if(element.id == event.id) + { + find=true; + Object.assign(element,event); + } + }); + if(!find) //se non l'ho trovato, lo aggiungo in fondo + EventQueue.unshift(event); + + if(!callInProgress) + showMessage(event); + break; + + case "remove": + + //se non c'è niente nella queue e non c'è una chiamata in corso + if( EventQueue.length==0 && !callInProgress) + next(); + + //se l'id è uguale a quello attualmente visualizzato ( e non siamo in chiamata ) + if(!callInProgress && EventQueue[0] !== undefined && EventQueue[0].id == event.id) + next(); //passo al messaggio successivo ( per la rimozione ci penserà la next ) + + else{ + //altrimenti rimuovo tutti gli elementi con quell'id( creando un nuovo array ) + let newEventQueue=[]; + EventQueue.forEach(element => { + if(element.id != event.id) + newEventQueue.push(element); + }); + EventQueue=newEventQueue; + } + + + + + break; + case "musicstate": + case "musicinfo": + + break; + } +}; + + + + + + +let showMessage = function(msg){ + LOG("showMessage"); + LOG(msg); + g.setBgColor(settings.colBg); + + + if(typeof msg.CanScrollDown==="undefined") + msg.CanScrollDown=false; + if(typeof msg.CanScrollUp==="undefined") + msg.CanScrollUp=false; + + + + + + // Normal text message display + let title=msg.title, titleFont = settings.fontLarge, lines; + if (title) { + let w = g.getWidth()-48; + if (g.setFont(titleFont).stringWidth(title) > w) + titleFont = settings.fontMedium; + if (g.setFont(titleFont).stringWidth(title) > w) { + lines = g.wrapString(title, w); + title = (lines.length>2) ? lines.slice(0,2).join("\n")+"..." : lines.join("\n"); + } + } + + + + let Layout = require("Layout"); + layout = new Layout({ type:"v", c: [ + {type:"h", fillx:1, bgCol:settings.colHeadBg, c: [ + { type:"btn", src:require("messageicons").getImage(msg), col:require("messageicons").getColor(msg), pad: 3}, + { type:"v", fillx:1, c: [ + {type:"txt", font:settings.fontSmall, label:msg.src||/*LANG*/"Message", bgCol:settings.colHeadBg, fillx:1, pad:2, halign:1 }, + title?{type:"txt", font:titleFont, label:title, bgCol:settings.colHeadBg, fillx:1, pad:2 }:{}, + ]}, + ]}, + {type:"v",fillx:1,filly:1,pad:2 ,halign:-1,c:[]}, + + + + + ]}); + + + if (!settings.quiet && msg.new) + { + msg.new=false; + Bangle.buzz(); + } + + + g.clearRect(Bangle.appRect); + layout.render(); + + PrintMessageStrings(msg); + Bangle.setLCDPower(1); + + DrawLock(); + +}; +let DrawLock=function() +{ + let w=8,h=8; + let x = g.getWidth()-w; + let y = 0; + if(Bangle.isLocked()) + g.setBgColor(settings.colLock); + else + g.setBgColor(settings.colHeadBg); + g.clearRect(x,y,x+w,y+h); +}; + + + + + + +let showCall = function(msg) +{ + LOG("showCall"); + LOG(msg); + // se anche prima era una call PrevMessage==msg.id + //non so perchè prima era cosi + if( msg.t=="remove") + { + LOG("hide call screen"); + next(); //dont shift + return; + } + + callInProgress=true; + + + + //se è una chiamata ( o una nuova chiamata, diversa dalla precedente ) + //la visualizzo + + let title=msg.title, titleFont = settings.fontLarge, lines; + if (title) { + let w = g.getWidth()-48; + if (g.setFont(titleFont).stringWidth(title) > w) + titleFont = settings.fontMedium; + if (g.setFont(titleFont).stringWidth(title) > w) { + lines = g.wrapString(title, w); + title = (lines.length>2) ? lines.slice(0,2).join("\n")+"..." : lines.join("\n"); + } + } + let Layout = require("Layout"); + layout = new Layout({ type:"v", c: [ + {type:"h", fillx:1, bgCol:settings.colHeadBg, c: [ + { type:"btn", src:require("messageicons").getImage(msg), col:require("messageicons").getColor(msg), pad: 3}, + { type:"v", fillx:1, c: [ + {type:"txt", font:settings.fontSmall, label:msg.src||/*LANG*/"Message", bgCol:settings.colHeadBg, fillx:1, pad:2, halign:1 }, + title?{type:"txt", font:titleFont, label:title, bgCol:settings.colHeadBg, fillx:1, pad:2 }:{}, + ]}, + ]}, + {type:"txt", font:settings.fontMedium, label:msg.body, fillx:1,filly:1,pad:2 ,halign:0} + ]}); + + + StopBuzzCall(); + if ( !settings.quiet ) { + if(msg.new) + { + msg.new=false; + CallBuzzTimer = setInterval(function() { + Bangle.buzz(500); + }, 1000); + + Bangle.buzz(500); + } + } + g.clearRect(Bangle.appRect); + layout.render(); + PrintMessageStrings(msg); + Bangle.setLCDPower(1); + DrawLock(); +}; + + + + + + + + + +let next=function(){ + LOG("next"); + StopBuzzCall(); + + + //se c'è una chiamata, non shifto + if(!callInProgress) + EventQueue.shift(); //passa al messaggio successivo, se presente - tolgo il primo + + callInProgress=false; + if( EventQueue.length == 0) + { + LOG("no element in queue - closing") + setTimeout(_ => load()); + return; + } + + + showMessage(EventQueue[0]); + +}; + + + + + + + + + + + +let showMapMessage=function(msg) { + + g.clearRect(Bangle.appRect); + PrintMessageStrings({body:"Not implemented!"}); + +} + + + + + +let CallBuzzTimer=null; +let StopBuzzCall=function() +{ + if (CallBuzzTimer){ + clearInterval(CallBuzzTimer); + CallBuzzTimer=null; + } +} +let DrawTriangleUp=function() +{ + g.fillPoly([169,46,164,56,174,56]); +} +let DrawTriangleDown=function() +{ + g.fillPoly([169,170,164,160,174,160]); +} + + + + +let ScrollUp=function() +{ + msg= EventQueue[0]; + + if(typeof msg.FirstLine==="undefined") + msg.FirstLine=0; + if(typeof msg.CanScrollUp==="undefined") + msg.CanScrollUp=false; + + if(!msg.CanScrollUp) return; + + msg.FirstLine = msg.FirstLine>0?msg.FirstLine-1:0; + + PrintMessageStrings(msg); +} +let ScrollDown=function() +{ + msg= EventQueue[0]; + if(typeof msg.FirstLine==="undefined") + msg.FirstLine=0; + if(typeof msg.CanScrollDown==="undefined") + msg.CanScrollDown=false; + + if(!msg.CanScrollDown) return; + + msg.FirstLine = msg.FirstLine+1; + PrintMessageStrings(msg); +} + + + + + + +let PrintMessageStrings=function(msg) +{ + let MyWrapString = function (str,maxWidth) + { + str=str.replace("\r\n","\n").replace("\r","\n"); + return g.wrapString(str,maxWidth); + } + + + if(typeof msg.FirstLine==="undefined") msg.FirstLine=0; + + let bodyFont = typeof msg.bodyFont==="undefined"? settings.fontMedium : msg.bodyFont; + let Padding=2; + if(typeof msg.lines==="undefined") + { + g.setFont(bodyFont); + msg.lines = MyWrapString(msg.body,g.getWidth()-(Padding*2)) + if ( msg.lines.length<=2) + { + bodyFont= g.getFonts().includes("Vector")?"Vector:20":"6x8:3"; + g.setFont(bodyFont); + msg.lines = MyWrapString(msg.body,g.getWidth()-(Padding*2)) + msg.bodyFont = bodyFont; + } + } + + + + //prendo le linee da stampare + let NumLines=8; + let linesToPrint = (msg.lines.length>NumLines) ? msg.lines.slice(msg.FirstLine,msg.FirstLine+NumLines):msg.lines; + + + let yText=45; + + //invalido l'area e disegno il testo + g.setBgColor(settings.colBg); + g.clearRect(0,yText,176,176); + let xText=Padding; + yText+=Padding; + g.setFont(bodyFont); + let HText=g.getFontHeight(); + + yText=((176-yText)/2)-(linesToPrint.length * HText / 2) + yText; + + if( linesToPrint.length<=2) + { + g.setFontAlign(0,-1); + xText = g.getWidth()/2; + } + else + g.setFontAlign(-1,-1); + + + linesToPrint.forEach((line, i)=>{ + g.drawString(line,xText,yText+HText*i); + }); + + //disegno le freccie + if(msg.FirstLine!=0) + { + msg.CanScrollUp=true; + DrawTriangleUp(); + } + else + msg.CanScrollUp=false; + + if(msg.FirstLine+linesToPrint.length < msg.lines.length) + { + msg.CanScrollDown=true; + DrawTriangleDown(); + } + else + msg.CanScrollDown=false; + + +} + + + + +let doubleTapUnlock=function(data) { + if( data.double) //solo se in double + { + Bangle.setLocked(false); + Bangle.setLCDPower(1); + } +} +let toushScroll=function(button, xy) { + let height=176; //g.getHeight(); -> 176 B2 + height/=2; + + if(xy.y next(), BTN1,{repeat: true}); + + //il tap è il tocco con l'accellerometro! + Bangle.on('tap', doubleTapUnlock); + Bangle.on('touch', toushScroll); + + //quando apro quest'app, do per scontato che c'è un messaggio da leggere posto in un file particolare ( NewMessage.json ) + let eventToShow = require('Storage').readJSON(settings.NewEventFileName, true); + require("Storage").erase(settings.NewEventFileName) + if( eventToShow!==undefined) + manageEvent(eventToShow); + else + { + LOG("file not found!"); + setTimeout(_ => load(), 0); + } +}; + + + + +main(); \ No newline at end of file diff --git a/apps/messages_light/messages_light.boot.js b/apps/messages_light/messages_light.boot.js new file mode 100644 index 000000000..741d08b96 --- /dev/null +++ b/apps/messages_light/messages_light.boot.js @@ -0,0 +1,33 @@ +/* +//OLD CODE -> backup purpose + +let messageBootManager=function(type,event){ + //se l'app non è aperta + if ("undefined"==typeof manageEvent) + { + if(event.t=="remove") return; //l'app non è aperta, non c'è nessun messaggio da rimuovere dalla queue -> non lancio l'app + + //la apro + require("Storage").writeJSON("messages_light.NewEvent.json",{"event":event,"type":type}); + load("messages_light.app.js"); + } + else + { + //altrimenti gli dico di gestire il messaggio + manageEvent(type,event); + } +} +Bangle.on("message", messageBootManager); +Bangle.on("call", messageBootManager);*/ + + + +//override require to filter require("message") +global.require_real=global.require; +global.require = (_require => file => { + if (file==="messages") file = "messagesProxy"; + //else if (file==="messages_REAL") file = "messages"; //backdoor to real message + + return _require(file); +})(require); + diff --git a/apps/messages_light/messages_light.messagesProxy.js b/apps/messages_light/messages_light.messagesProxy.js new file mode 100644 index 000000000..723397057 --- /dev/null +++ b/apps/messages_light/messages_light.messagesProxy.js @@ -0,0 +1,30 @@ + +//gestisco il messaggio a modo mio +exports.pushMessage = function(event) { + + //TODO: now i can't handle the music, so i call the real message app + if( event.id=="music") return require_real("messages").pushMessage(event); + + //se l'app non è aperta + if ("undefined"==typeof manageEvent) + { + if(event.t=="remove") return; //l'app non è aperta, non c'è nessun messaggio da rimuovere dalla queue -> non lancio l'app + + //la apro + require_real("Storage").writeJSON("messages_light.NewEvent.json",event); + load("messages_light.app.js"); + } + else + { + //altrimenti gli dico di gestire il messaggio + manageEvent(event); + } +} + + +//Call original message library +exports.clearAll = function() { return require_real("messages").clearAll()} +exports.getMessages = function() { return require_real("messages").getMessages()} +exports.status = function() { return require_real("messages").status()} +exports.buzz = function() { return require_real("messages").buzz(msgSrc)} +exports.stopBuzz = function() { return require_real("messages").stopBuzz()} \ No newline at end of file diff --git a/apps/messages_light/messages_light.settings.js b/apps/messages_light/messages_light.settings.js new file mode 100644 index 000000000..b7197c70a --- /dev/null +++ b/apps/messages_light/messages_light.settings.js @@ -0,0 +1 @@ +eval(require("Storage").read("messages.settings.js")); diff --git a/apps/messages_light/metadata.json b/apps/messages_light/metadata.json new file mode 100644 index 000000000..9b7ca50a8 --- /dev/null +++ b/apps/messages_light/metadata.json @@ -0,0 +1,22 @@ +{ + "id": "messages_light", + "name": "Messages Light", + "version": "1.3", + "description": "A light implementation of messages App (display notifications from iOS and Gadgetbridge/Android)", + "icon": "app.png", + "type": "app", + "tags": "tool,system", + "supports": ["BANGLEJS","BANGLEJS2"], + "dependencies" : { "messageicons":"module","messages":"app" }, + "readme": "README.md", + "storage": [ + {"name":"messages_light.app.js","url":"messages_light.app.js"}, + {"name":"messages_light.settings.js","url":"messages_light.settings.js"}, + {"name":"messages_light.img","url":"app-icon.js","evaluate":true}, + {"name":"messagesProxy","url":"messages_light.messagesProxy.js"}, + {"name":"messages_light.boot.js","url":"messages_light.boot.js"} + ], + "data": [{"name":"messages_light.settings.json"},{"name":"messages_light.NewMessage.json"}], + "screenshots": [{"url":"screenshot-notify.png"} ,{"url":"screenshot-long-text1.png"},{"url":"screenshot-long-text2.png"}, {"url":"screenshot-call.png"} ], + "sortorder": -9 +} diff --git a/apps/messages_light/screenshot-call.png b/apps/messages_light/screenshot-call.png new file mode 100644 index 000000000..703faad6f Binary files /dev/null and b/apps/messages_light/screenshot-call.png differ diff --git a/apps/messages_light/screenshot-long-text1.png b/apps/messages_light/screenshot-long-text1.png new file mode 100644 index 000000000..147b0cd5c Binary files /dev/null and b/apps/messages_light/screenshot-long-text1.png differ diff --git a/apps/messages_light/screenshot-long-text2.png b/apps/messages_light/screenshot-long-text2.png new file mode 100644 index 000000000..5408f2059 Binary files /dev/null and b/apps/messages_light/screenshot-long-text2.png differ diff --git a/apps/messages_light/screenshot-notify.png b/apps/messages_light/screenshot-notify.png new file mode 100644 index 000000000..8896b803a Binary files /dev/null and b/apps/messages_light/screenshot-notify.png differ