forked from FOSS/BangleApps
Add files via upload
parent
25bd4fb752
commit
d69d6098a4
|
@ -0,0 +1,8 @@
|
||||||
|
0.01: New Widget!
|
||||||
|
0.02: Version using connect back
|
||||||
|
0.03: Version using modified firmware
|
||||||
|
0.04: Works on both standard and modified firmware
|
||||||
|
0.05: Bug fixes w.r.t. reconnection
|
||||||
|
0.06: Update README - Release version
|
||||||
|
|
||||||
|
|
|
@ -1 +1,70 @@
|
||||||
temporary
|
## ANCS - iPhone notifications for Bangle.js
|
||||||
|
|
||||||
|
The ANCS widget allows you to answer or cancel iPhone incoming calls and also displays messages and notifications. It connects to the Apple Notification Center Service which is already on all iPhones, so you do not need to install any additional iPhone apps to use this widget.
|
||||||
|
|
||||||
|
## Firmware
|
||||||
|
The widget will run on the standard firmware, however, installation of a slightly modified version - the zip file is available from [this directory](https://github.com/jeffmer/JeffsBangleAppsDev/tree/master/apps/widancs) - will increase the performance of the app by an order of magnitude in terms of the time to connect or reconnect to the iPhone. In addition, the Bangle will stay connected to the iPhone over a greater separation distance than with the standard firmware.
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
After the widget is uploaded to the Bangle, it needs to be enabled in the Bangle Settings app:- `ANCS Widget` will appear in `APP/Widget settings`. There is also a menu in these settings to let you configure the categories of notifications that you want to be displayed. You must disconnect from the App Loader before enabling the widget.
|
||||||
|
|
||||||
|
## Compatible Apps
|
||||||
|
|
||||||
|
The widget will only run with a compatible app - for the reason for this see Issue 1 below. The apps that are compatible with the ANCS widget are:- **Multi Clock**, **Navigation Compass** and **GPS Navigation**. When you switch to an app that is not compatible, the ANCS phone icon will not appear.
|
||||||
|
|
||||||
|
## iPhone Pairing
|
||||||
|
Once enabled, the widget icon should be displayed coloured grey (its green in the photo). Go to the phone's Bluetooth settings menu and your Bangle should appear under Other devices. If this is the first time you have connected with the Bangle from your iPhone, it may be named Accessory. Click on the name and the iPhone should connect and start pairing. The widget icon will turn red and the iPhone will ask you to enter a pairing code - the traditional 123456. You have 10 seconds to enter this after which you will need to start pairing again. After that, the iPhone may also ask to allow the device access to ANCS. Once pairing is complete, the widget icon should go blue and eventually green. The range of colours is:
|
||||||
|
|
||||||
|
* **Grey** - not connected - advertising
|
||||||
|
* **Red** - connected - not paired.
|
||||||
|
* **Blue** - paired and connected - getting services
|
||||||
|
* **Yellow** - got Services.
|
||||||
|
* **Green** - waiting for new notifications.
|
||||||
|
|
||||||
|
After pairing the first time, the Bangle should connect automatically when the widget is running. Sometimes you may need you to click on the Bangle name in `Settings:Bluetooth:My devices` on the iPhone or disable and then enable Bluetooth to start connection. If you need to load other apps from the iPhone, it will be necessary to ask the iPhone to forget the pairing and you will also need to disable the widget in Settings and restart the Bangle by turning it off in Settings and then pressing BTN1 to restart. If you are loading apps from a different device, you simply need to turn off the iPhone bluetooth which will retain the pairing. You still need to disable the widget and restart the Bangle.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Messages & Calls
|
||||||
|
Messages are displayed as shown above until BTN2 is pressed to dismiss it. I strongly advise disabling the BTN2 LCD wake function in the Settings App as otherwise when the screen times out and you press BTN2 to wake the LCD, the screen will turn on and the Message Alert will be dismissed!. Calls can be answered or dropped.
|
||||||
|
|
||||||
|
 
|
||||||
|
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
1. With GadgetBridge, the Android phone has a Central-Client role with the Bangle as Peripheral-Server. With the ANCS widget there is the fairly unusual situation in which the Bangle is Peripheral-Client to the iPhone's Central-Server role. Since Espruino does not deal explicitly with Bangle as Peripheral-Client an additional function has been added in the modified firmware: `var gatt = NRF.getGattforCentralServer(addr);`. This returns a bluetooth remote GATT server given the address of the iPhone which has just connected to the Bangle. With the standard firmware, the widget reconnects to the iPhone as a Client - however this has greatly degraded performance. See [Issue 1800.](https://github.com/espruino/Espruino/issues/1800) for more details.
|
||||||
|
|
||||||
|
2. When the Bangle switches apps, all state - including widget state - is lost unless explicitly stored. The consequence of this is that when the Bangle switches apps, the connection to iPhone has to be re-established to restore the remote GATT server and characteristics state. This is quite slow. To minimise reconnection, the widget needs to grab the screen from the running app to signal messages and calls. To allow this to work, the app needs to implement the `SCREENACCESS` interface. In essence, the widget only connects when running with compatible apps that implement this interface. An example implementation is:
|
||||||
|
|
||||||
|
```
|
||||||
|
var SCREENACCESS = {
|
||||||
|
withApp:true,
|
||||||
|
request:function(){
|
||||||
|
this.withApp=false;
|
||||||
|
stopdraw(); //clears redraw timers etc
|
||||||
|
clearWatch(); //clears button handlers
|
||||||
|
},
|
||||||
|
release:function(){
|
||||||
|
this.withApp=true;
|
||||||
|
startdraw(); //redraw app screen, restart timers etc
|
||||||
|
setButtons(); //install button event handlers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.on('lcdPower',function(on) {
|
||||||
|
if (!SCREENACCESS.withApp) return;
|
||||||
|
if (on) {
|
||||||
|
startdraw();
|
||||||
|
} else {
|
||||||
|
stopdraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Please report bugs etc. by raising an issue [here](https://github.com/jeffmer/JeffsBangleAppsDev).
|
|
@ -0,0 +1,264 @@
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
var s = require("Storage").readJSON("widancs.json",1)||{settings:{enabled:false, category:[1,2,4]}};
|
||||||
|
var ENABLED = s.settings.enabled;
|
||||||
|
var CATEGORY = s.settings.category;
|
||||||
|
|
||||||
|
function advert(){
|
||||||
|
NRF.setAdvertising([
|
||||||
|
0x02, //length
|
||||||
|
0x01, //flags
|
||||||
|
0x06, //
|
||||||
|
0x11, //length
|
||||||
|
0x15, //solicited Service UUID
|
||||||
|
0xD0,0x00,0x2D,0x12,0x1E,0x4B,
|
||||||
|
0x0F,0xA4,
|
||||||
|
0x99,0x4E,
|
||||||
|
0xCE,0xB5,
|
||||||
|
0x31,0xF4,0x05,0x79],{connectable:true,discoverable:true,interval:375});
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = {
|
||||||
|
gatt:null,
|
||||||
|
ancs:null,
|
||||||
|
current:{cat:0,uid:0},
|
||||||
|
notqueue:[],
|
||||||
|
msgTO:undefined,
|
||||||
|
com:new Uint8Array([0,0,0,0,0,1,20,0,3,100,0]),
|
||||||
|
buf:new Uint8Array(132),
|
||||||
|
inp:0,
|
||||||
|
store:function(b){
|
||||||
|
var i = this.inp;
|
||||||
|
if (i+b.length<=132){
|
||||||
|
this.buf.set(b,i);
|
||||||
|
this.inp+=b.length;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gotmsg:function(){
|
||||||
|
var n = this.inp;
|
||||||
|
var vw = DataView(this.buf.buffer);
|
||||||
|
if (n<8) return null;
|
||||||
|
var tn=vw.getUint16(6,true);
|
||||||
|
if (n<(tn+8)) return null;
|
||||||
|
var mn=vw.getUint16(9+tn,true);
|
||||||
|
if (n<(mn+tn+11)) return null;
|
||||||
|
return {tlen:tn, mlen:mn};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//stop advertising when peripheral link disconnected
|
||||||
|
if (!NRF.getGattforCentralServer && ENABLED && typeof SCREENACCESS!='undefined')
|
||||||
|
NRF.on('disconnect',function(reason){
|
||||||
|
NRF.sleep();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ENABLED && typeof SCREENACCESS!='undefined')
|
||||||
|
NRF.on('connect',function(addr){
|
||||||
|
if(NRF.getGattforCentralServer)
|
||||||
|
do_bond(NRF.getGattforCentralServer(addr));
|
||||||
|
else
|
||||||
|
NRF.connect(addr).then(do_bond);
|
||||||
|
});
|
||||||
|
|
||||||
|
function do_bond(g) {
|
||||||
|
var tval, ival;
|
||||||
|
state.gatt = g;
|
||||||
|
function cleanup(){
|
||||||
|
drawIcon(0); //disconnect from iPhone
|
||||||
|
delete state.gatt;
|
||||||
|
delete state.ancs;
|
||||||
|
if(!NRF.getGattforCentralServer) NRF.disconnect();
|
||||||
|
setTimeout(()=>{NRF.wake();},500);
|
||||||
|
}
|
||||||
|
drawIcon(1); //connect from iPhone
|
||||||
|
state.gatt.device.on('gattserverdisconnected', function(reason) {
|
||||||
|
if (ival) clearInterval(ival);
|
||||||
|
if (tval) clearInterval(tval);
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
E.on("kill",function(){
|
||||||
|
state.gatt.disconnect().then(function(){NRF.sleep();});
|
||||||
|
});
|
||||||
|
NRF.setSecurity({passkey:"123456",mitm:1,display:1});
|
||||||
|
tval = setTimeout(function(){
|
||||||
|
if (ival) clearInterval(ival);
|
||||||
|
state.gatt.disconnect().then(cleanup);
|
||||||
|
},10000);
|
||||||
|
state.gatt.startBonding().then(function(){
|
||||||
|
ival = setInterval(function(){
|
||||||
|
var sec = state.gatt.getSecurityStatus();
|
||||||
|
if (!sec.connected) {clearInterval(ival); clearTimeout(tval); return;}
|
||||||
|
if (sec.connected && sec.encrypted){
|
||||||
|
clearInterval(ival);
|
||||||
|
clearTimeout(tval);
|
||||||
|
drawIcon(2); //bonded to iPhone
|
||||||
|
do_ancs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},1000);
|
||||||
|
}).catch(function(e){
|
||||||
|
Terminal.println("ERROR "+e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_ancs() {
|
||||||
|
state.ancs = {primary:null, notify:null, control:null, data:null};
|
||||||
|
state.gatt.getPrimaryService("7905F431-B5CE-4E99-A40F-4B1E122D00D0").then(function(s) {
|
||||||
|
state.ancs.primary=s;
|
||||||
|
return s.getCharacteristic("9FBF120D-6301-42D9-8C58-25E699A21DBD");
|
||||||
|
}).then(function(c) {
|
||||||
|
state.ancs.notify=c;
|
||||||
|
return state.ancs.primary.getCharacteristic("69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9");
|
||||||
|
}).then(function(c) {
|
||||||
|
state.ancs.control=c;
|
||||||
|
return state.ancs.primary.getCharacteristic("22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB");
|
||||||
|
}).then(function(c) {
|
||||||
|
state.ancs.data =c;
|
||||||
|
drawIcon(3);//got remote services
|
||||||
|
state.ancs.notify.on('characteristicvaluechanged', function(ev) {
|
||||||
|
getnotify(ev.target.value);
|
||||||
|
});
|
||||||
|
state.ancs.data.on('characteristicvaluechanged', function(e) {
|
||||||
|
state.store(e.target.value.buffer);
|
||||||
|
var inds = state.gotmsg();
|
||||||
|
if (inds) printmsg(state.buf,inds);
|
||||||
|
});
|
||||||
|
state.ancs.notify.startNotifications().then(function(){
|
||||||
|
state.ancs.data.startNotifications().then(function(){
|
||||||
|
drawIcon(4); //ready for messages
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
Terminal.println("ERROR "+e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function wordwrap(s){
|
||||||
|
var txt = s.split("\n");
|
||||||
|
var MAXCHARS = 18;
|
||||||
|
for (var i = 0; i < txt.length; i++) {
|
||||||
|
txt[i] = txt[i].trim();
|
||||||
|
var l = txt[i];
|
||||||
|
if (l.length > MAXCHARS) {
|
||||||
|
var p = MAXCHARS;
|
||||||
|
while (p > MAXCHARS - 8 && !" \t-_".includes(l[p]))
|
||||||
|
p--;
|
||||||
|
if (p == MAXCHARS - 8) p = MAXCHARS;
|
||||||
|
txt[i] = l.substr(0, p);
|
||||||
|
txt.splice(i + 1, 0, l.substr(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return txt.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var buzzing =false;
|
||||||
|
var screentimeout = undefined;
|
||||||
|
var inalert = false;
|
||||||
|
|
||||||
|
function release_screen(){
|
||||||
|
screentimeout= setTimeout(() => {
|
||||||
|
SCREENACCESS.release();
|
||||||
|
screentimeout = undefined;
|
||||||
|
inalert=false;
|
||||||
|
next_notify();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function printmsg(buf,inds){
|
||||||
|
|
||||||
|
function send_action(tf){
|
||||||
|
var bb = new Uint8Array(6);
|
||||||
|
var v = DataView(bb.buffer);
|
||||||
|
v.setUint8(0,2);
|
||||||
|
v.setUint32(1,state.current.uid,true);
|
||||||
|
v.setUint8(5,tf?0:1 );
|
||||||
|
state.ancs.control.writeValue(bb).then(release_screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.msgTO) clearTimeout(state.msgTO);
|
||||||
|
var title="";
|
||||||
|
for (var i=8;i<8+inds.tlen; ++i) title+=String.fromCharCode(buf[i]);
|
||||||
|
var message = "";
|
||||||
|
for (var j=11+inds.tlen;j<11+inds.tlen+inds.mlen;++j) {
|
||||||
|
message+=String.fromCharCode(buf[j]);
|
||||||
|
}
|
||||||
|
message = wordwrap(message);
|
||||||
|
//we may already be displaying a prompt, so clear it
|
||||||
|
E.showPrompt();
|
||||||
|
if (screentimeout) clearTimeout(screentimeout);
|
||||||
|
Bangle.setLCDPower(true);
|
||||||
|
SCREENACCESS.request();
|
||||||
|
if (!buzzing){
|
||||||
|
buzzing=true;
|
||||||
|
Bangle.buzz(500).then(()=>{buzzing=false;});
|
||||||
|
}
|
||||||
|
if (state.current.cat!=1){
|
||||||
|
E.showAlert(message,title).then(send_action.bind(null,false));
|
||||||
|
} else {
|
||||||
|
E.showPrompt(message,{title:title,buttons:{"Accept":true,"Cancel":false}}).then(send_action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var notifyTO;
|
||||||
|
function getnotify(d){
|
||||||
|
var eid = d.getUint8(0);
|
||||||
|
var ct = d.getUint8(2);
|
||||||
|
var id = d.getUint32(4,true);
|
||||||
|
if (eid>1) return;
|
||||||
|
if (notifyTO) clearTimeout(notifyTO);
|
||||||
|
if(!CATEGORY.includes(ct)) return;
|
||||||
|
var len = state.notqueue.length;
|
||||||
|
if (ct == 1) { // it's a call so pre-empt
|
||||||
|
if (inalert) {state.notqueue.push(state.current); inalert=false;}
|
||||||
|
state.notqueue.push({cat:ct, uid:id});
|
||||||
|
} else if (len<16)
|
||||||
|
state.notqueue[len] = {cat:ct, uid:id};
|
||||||
|
notifyTO = setTimeout(next_notify,1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function next_notify(){
|
||||||
|
if(state.notqueue.length==0 || inalert) return;
|
||||||
|
inalert=true;
|
||||||
|
state.current = state.notqueue.pop();
|
||||||
|
var v = DataView(state.com.buffer);
|
||||||
|
if (state.current.cat==6) v.setUint8(8,2); else v.setUint8(8,3);//get email title
|
||||||
|
v.setUint32(1,state.current.uid,true);
|
||||||
|
state.inp=0;
|
||||||
|
state.ancs.control.writeValue(state.com).then(function(){
|
||||||
|
state.msgTO=setTimeout(()=>{
|
||||||
|
inalert=false;
|
||||||
|
state.msgTO=undefined;
|
||||||
|
next_notify();
|
||||||
|
},1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var stage = 5;
|
||||||
|
//grey, pink, lightblue, yellow, green
|
||||||
|
function draw(){
|
||||||
|
var colors = new Uint16Array([0xc618,0xf818,0x3ff,0xffe0,0x07e0,0x0000]);
|
||||||
|
var img = E.toArrayBuffer(atob("GBgBAAAABAAADgAAHwAAPwAAf4AAP4AAP4AAP4AAHwAAH4AAD8AAB+AAA/AAAfgAAf3gAH/4AD/8AB/+AA/8AAf4AAHwAAAgAAAA"));
|
||||||
|
g.setColor(colors[stage]);
|
||||||
|
g.drawImage(img,this.x,this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
WIDGETS["ancs"] ={area:"tl", width:24,draw:draw};
|
||||||
|
|
||||||
|
function drawIcon(id){
|
||||||
|
stage = id;
|
||||||
|
WIDGETS["ancs"].draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ENABLED && typeof SCREENACCESS!='undefined') {
|
||||||
|
stage = 0;
|
||||||
|
NRF.setServices(undefined,{uart:false});
|
||||||
|
NRF.sleep();
|
||||||
|
NRF.wake();
|
||||||
|
advert();
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
(function(){function t(a){function e(){k(0);delete b.gatt;delete b.ancs;NRF.getGattforCentralServer||NRF.disconnect();setTimeout(function(){NRF.wake()},500)}var d;b.gatt=a;k(1);b.gatt.device.on("gattserverdisconnected",function(a){d&&clearInterval(d);c&&clearInterval(c);e()});E.on("kill",function(){b.gatt.disconnect().then(function(){NRF.sleep()})});NRF.setSecurity({passkey:"123456",mitm:1,display:1});var c=setTimeout(function(){d&&clearInterval(d);b.gatt.disconnect().then(e)},1E4);b.gatt.startBonding().then(function(){d=
|
||||||
|
setInterval(function(){var a=b.gatt.getSecurityStatus();a.connected?a.connected&&a.encrypted&&(clearInterval(d),clearTimeout(c),k(2),v()):(clearInterval(d),clearTimeout(c))},1E3)})["catch"](function(a){Terminal.println("ERROR "+a)})}function v(){b.ancs={primary:null,notify:null,control:null,data:null};b.gatt.getPrimaryService("7905F431-B5CE-4E99-A40F-4B1E122D00D0").then(function(a){b.ancs.primary=a;return a.getCharacteristic("9FBF120D-6301-42D9-8C58-25E699A21DBD")}).then(function(a){b.ancs.notify=
|
||||||
|
a;return b.ancs.primary.getCharacteristic("69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9")}).then(function(a){b.ancs.control=a;return b.ancs.primary.getCharacteristic("22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB")}).then(function(a){b.ancs.data=a;k(3);b.ancs.notify.on("characteristicvaluechanged",function(a){var e=a.target.value,c=e.getUint8(0);a=e.getUint8(2);e=e.getUint32(4,!0);1<c||(m&&clearTimeout(m),w.includes(a)&&(c=b.notqueue.length,1==a?(f&&(b.notqueue.push(b.current),f=!1),b.notqueue.push({cat:a,uid:e})):
|
||||||
|
16>c&&(b.notqueue[c]={cat:a,uid:e}),m=setTimeout(n,1E3)))});b.ancs.data.on("characteristicvaluechanged",function(a){b.store(a.target.value.buffer);(a=b.gotmsg())&&x(b.buf,a)});b.ancs.notify.startNotifications().then(function(){b.ancs.data.startNotifications().then(function(){k(4)})})})["catch"](function(a){Terminal.println("ERROR "+a)})}function y(a){a=a.split("\n");for(var b=0;b<a.length;b++){a[b]=a[b].trim();var d=a[b];if(18<d.length){for(var c=18;10<c&&!" \t-_".includes(d[c]);)c--;10==c&&(c=18);
|
||||||
|
a[b]=d.substr(0,c);a.splice(b+1,0,d.substr(c))}}return a.join("\n")}function z(){l=setTimeout(function(){SCREENACCESS.release();l=void 0;f=!1;n()},500)}function x(a,e){function d(a){var e=new Uint8Array(6),c=DataView(e.buffer);c.setUint8(0,2);c.setUint32(1,b.current.uid,!0);c.setUint8(5,a?0:1);b.ancs.control.writeValue(e).then(z)}b.msgTO&&clearTimeout(b.msgTO);for(var c="",h=8;h<8+e.tlen;++h)c+=String.fromCharCode(a[h]);h="";for(var f=11+e.tlen;f<11+e.tlen+e.mlen;++f)h+=String.fromCharCode(a[f]);
|
||||||
|
h=y(h);E.showPrompt();l&&clearTimeout(l);Bangle.setLCDPower(!0);SCREENACCESS.request();p||(p=!0,Bangle.buzz(500).then(function(){p=!1}));1!=b.current.cat?E.showAlert(h,c).then(d.bind(null,!1)):E.showPrompt(h,{title:c,buttons:{Accept:!0,Cancel:!1}}).then(d)}function n(){if(0!=b.notqueue.length&&!f){f=!0;b.current=b.notqueue.pop();var a=DataView(b.com.buffer);6==b.current.cat?a.setUint8(8,2):a.setUint8(8,3);a.setUint32(1,b.current.uid,!0);b.inp=0;b.ancs.control.writeValue(b.com).then(function(){b.msgTO=
|
||||||
|
setTimeout(function(){f=!1;b.msgTO=void 0;n()},1E3)})}}function k(a){q=a;WIDGETS.ancs.draw()}var u=require("Storage").readJSON("widancs.json",1)||{settings:{enabled:!1,category:[1,2,4]}},r=u.settings.enabled,w=u.settings.category,b={gatt:null,ancs:null,current:{cat:0,uid:0},notqueue:[],msgTO:void 0,com:new Uint8Array([0,0,0,0,0,1,20,0,3,100,0]),buf:new Uint8Array(132),inp:0,store:function(a){var b=this.inp;132>=b+a.length&&(this.buf.set(a,b),this.inp+=a.length)},gotmsg:function(){var a=this.inp,b=
|
||||||
|
DataView(this.buf.buffer);if(8>a)return null;var d=b.getUint16(6,!0);if(a<d+8)return null;b=b.getUint16(9+d,!0);return a<b+d+11?null:{tlen:d,mlen:b}}};if(!NRF.getGattforCentralServer&&r&&"undefined"!=typeof SCREENACCESS)NRF.on("disconnect",function(a){NRF.sleep()});if(r&&"undefined"!=typeof SCREENACCESS)NRF.on("connect",function(a){NRF.getGattforCentralServer?t(NRF.getGattforCentralServer(a)):NRF.connect(a).then(t)});var p=!1,l=void 0,f=!1,m,q=5;WIDGETS.ancs={area:"tl",width:24,draw:function(){var a=
|
||||||
|
new Uint16Array([50712,63512,1023,65504,2016,0]),b=E.toArrayBuffer(atob("GBgBAAAABAAADgAAHwAAPwAAf4AAP4AAP4AAP4AAHwAAH4AAD8AAB+AAA/AAAfgAAf3gAH/4AD/8AB/+AA/8AAf4AAHwAAAgAAAA"));g.setColor(a[q]);g.drawImage(b,this.x,this.y)}};r&&"undefined"!=typeof SCREENACCESS&&(q=0,NRF.setServices(void 0,{uart:!1}),NRF.sleep(),NRF.wake(),NRF.setAdvertising([2,1,6,17,21,208,0,45,18,30,75,15,164,153,78,206,181,49,244,5,121],{connectable:!0,discoverable:!0,interval:375}))})();
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1,61 @@
|
||||||
|
(function(back) {
|
||||||
|
const ANCSFILE = "widancs.json";
|
||||||
|
|
||||||
|
// initialize with default settings...
|
||||||
|
let s = {
|
||||||
|
'enabled': false,
|
||||||
|
'category':[1,2,4]
|
||||||
|
};
|
||||||
|
// ...and overwrite them with any saved values
|
||||||
|
// This way saved values are preserved if a new version adds more settings
|
||||||
|
const storage = require('Storage');
|
||||||
|
const d = storage.readJSON(ANCSFILE, 1) || {};
|
||||||
|
const saved = d.settings || {};
|
||||||
|
for (const key in saved) {
|
||||||
|
s[key] = saved[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
d.settings = s;
|
||||||
|
storage.write(ANCSFILE, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setcategory(){
|
||||||
|
const names = ["Other","Call ","Missed Call","Voicemail","Messages ","Calendar","Email","News ","Fitness ","Busniness","Location ","Entertainment"];
|
||||||
|
function hascat(n){return s.category.includes(n);}
|
||||||
|
function setcat(n,v){
|
||||||
|
if (v)
|
||||||
|
s.category.push(n);
|
||||||
|
else
|
||||||
|
s.category = s.category.filter((v,i,a)=>{return v!=n;});
|
||||||
|
}
|
||||||
|
const menu = {
|
||||||
|
'': { 'title': 'Set Categories' }
|
||||||
|
};
|
||||||
|
for (var i=0; i<names.length;++i)
|
||||||
|
menu[names[i]]={
|
||||||
|
value:hascat(i),
|
||||||
|
format:v=>v?'Yes':'No',
|
||||||
|
onchange:setcat.bind(null,i)
|
||||||
|
};
|
||||||
|
menu['< Back'] = ()=>{save(); showMain();};
|
||||||
|
return E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMain(){
|
||||||
|
return E.showMenu({
|
||||||
|
'Enable ANCS': {
|
||||||
|
value: s.enabled,
|
||||||
|
format: () => (s.enabled ? 'Yes' : 'No'),
|
||||||
|
onchange: () => {
|
||||||
|
s.enabled = !s.enabled;
|
||||||
|
save();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Set Category':setcategory,
|
||||||
|
'< Back': back,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showMain();
|
||||||
|
});
|
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
Loading…
Reference in New Issue