mirror of https://github.com/espruino/BangleApps
Added ability to specify dependencies (used for `notify` at the moment)
Gadgetbridge: Modified to use the 'notify' librarypull/486/head
parent
3c41379206
commit
519262793d
|
@ -23,3 +23,4 @@ Changed for individual apps are listed in `apps/appname/ChangeLog`
|
||||||
* Fix `marked is not defined` error (and include in repo, just in case)
|
* Fix `marked is not defined` error (and include in repo, just in case)
|
||||||
* Fix error in 'Install Default Apps' if Flash storage is full enough that erasing takes a while
|
* Fix error in 'Install Default Apps' if Flash storage is full enough that erasing takes a while
|
||||||
* Fixed animated progress bar on app removal
|
* Fixed animated progress bar on app removal
|
||||||
|
* Added ability to specify dependencies (used for `notify` at the moment)
|
||||||
|
|
|
@ -197,6 +197,7 @@ and which gives information about the app for the Launcher.
|
||||||
"type":"widget/clock/app", // optional, default "app"
|
"type":"widget/clock/app", // optional, default "app"
|
||||||
// if this is 'widget' then it's not displayed in the menu
|
// if this is 'widget' then it's not displayed in the menu
|
||||||
// if it's 'clock' then it'll be loaded by default at boot time
|
// if it's 'clock' then it'll be loaded by default at boot time
|
||||||
|
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on
|
||||||
"version":"1.23",
|
"version":"1.23",
|
||||||
// added by BangleApps loader on upload based on apps.json
|
// added by BangleApps loader on upload based on apps.json
|
||||||
"files:"file1,file2,file3",
|
"files:"file1,file2,file3",
|
||||||
|
|
|
@ -109,10 +109,11 @@
|
||||||
{ "id": "gbridge",
|
{ "id": "gbridge",
|
||||||
"name": "Gadgetbridge",
|
"name": "Gadgetbridge",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.12",
|
"version":"0.13",
|
||||||
"description": "The default notification handler for Gadgetbridge notifications from Android",
|
"description": "The default notification handler for Gadgetbridge notifications from Android",
|
||||||
"tags": "tool,system,android,widget",
|
"tags": "tool,system,android,widget",
|
||||||
"type":"widget",
|
"type":"widget",
|
||||||
|
"dependencies": { "notify":"type" },
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"gbridge.settings.js","url":"settings.js"},
|
{"name":"gbridge.settings.js","url":"settings.js"},
|
||||||
{"name":"gbridge.img","url":"app-icon.js","evaluate":true},
|
{"name":"gbridge.img","url":"app-icon.js","evaluate":true},
|
||||||
|
|
|
@ -11,3 +11,4 @@
|
||||||
0.10: Make widget play well with other Gadgetbridge widgets/apps
|
0.10: Make widget play well with other Gadgetbridge widgets/apps
|
||||||
0.11: Report battery status on connect and at regular intervals
|
0.11: Report battery status on connect and at regular intervals
|
||||||
0.12: Setting to show/hide icon
|
0.12: Setting to show/hide icon
|
||||||
|
0.13: Modified to use the 'notify' library
|
||||||
|
|
|
@ -24,84 +24,8 @@
|
||||||
Bluetooth.println(JSON.stringify(message));
|
Bluetooth.println(JSON.stringify(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
function showNotification(size, render, turnOn) {
|
|
||||||
if (turnOn === undefined) turnOn = true
|
|
||||||
var oldMode = Bangle.getLCDMode();
|
|
||||||
|
|
||||||
Bangle.setLCDMode("direct");
|
|
||||||
g.setClipRect(0, 240, 239, 319);
|
|
||||||
g.setColor("#222222");
|
|
||||||
g.fillRect(1, 241, 238, 318);
|
|
||||||
|
|
||||||
render(320 - size);
|
|
||||||
|
|
||||||
g.setColor("#ffffff");
|
|
||||||
g.fillRect(0, 240, 1, 319);
|
|
||||||
g.fillRect(238, 240, 239, 319);
|
|
||||||
g.fillRect(2, 318, 238, 319);
|
|
||||||
|
|
||||||
if (turnOn) Bangle.setLCDPower(1); // light up
|
|
||||||
Bangle.setLCDMode(oldMode); // clears cliprect
|
|
||||||
|
|
||||||
function anim() {
|
|
||||||
state.scrollPos -= 2;
|
|
||||||
if (state.scrollPos < -size) {
|
|
||||||
state.scrollPos = -size;
|
|
||||||
}
|
|
||||||
Bangle.setLCDOffset(state.scrollPos);
|
|
||||||
if (state.scrollPos > -size) setTimeout(anim, 15);
|
|
||||||
}
|
|
||||||
anim();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideNotification() {
|
|
||||||
function anim() {
|
|
||||||
state.scrollPos += 4;
|
|
||||||
if (state.scrollPos > 0) state.scrollPos = 0;
|
|
||||||
Bangle.setLCDOffset(state.scrollPos);
|
|
||||||
if (state.scrollPos < 0) setTimeout(anim, 10);
|
|
||||||
}
|
|
||||||
anim();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleNotificationEvent(event) {
|
function handleNotificationEvent(event) {
|
||||||
|
require("notify").show(event);
|
||||||
// split text up at word boundaries
|
|
||||||
var txt = event.body.split("\n");
|
|
||||||
var MAXCHARS = 38;
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showNotification(80, (y) => {
|
|
||||||
|
|
||||||
// TODO: icon based on src?
|
|
||||||
var x = 120;
|
|
||||||
g.setFontAlign(0, 0);
|
|
||||||
g.setFont("6x8", 1);
|
|
||||||
g.setColor("#40d040");
|
|
||||||
g.drawString(event.src, x, y + 7);
|
|
||||||
|
|
||||||
g.setColor("#ffffff");
|
|
||||||
g.setFont("6x8", 2);
|
|
||||||
if (event.title)
|
|
||||||
g.drawString(event.title.slice(0,17), x, y + 25);
|
|
||||||
|
|
||||||
g.setFont("6x8", 1);
|
|
||||||
g.setColor("#ffffff");
|
|
||||||
g.setFontAlign(-1, -1);
|
|
||||||
g.drawString(txt.join("\n"), 10, y + 40);
|
|
||||||
});
|
|
||||||
|
|
||||||
Bangle.buzz();
|
Bangle.buzz();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,45 +34,26 @@
|
||||||
state.music = event.state
|
state.music = event.state
|
||||||
|
|
||||||
if (state.music === "play") {
|
if (state.music === "play") {
|
||||||
showNotification(40, (y) => {
|
require("notify").show({size:40, render:y => {
|
||||||
g.setColor("#ffffff");
|
g.setColor(-1);
|
||||||
g.drawImage(require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")), 8, y + 8);
|
g.drawImage(require("heatshrink").decompress(atob("jEYwILI/EAv/8gP/ARcMgOAASN8h+A/kfwP8n4CD/E/gHgjg/HA=")), 8, y + 8);
|
||||||
|
|
||||||
g.setFontAlign(-1, -1);
|
g.setFontAlign(-1, -1);
|
||||||
var x = 40;
|
var x = 40;
|
||||||
g.setFont("4x6", 2);
|
g.setFont("4x6", 2).drawString(state.musicInfo.artist, x, y + 8);
|
||||||
g.setColor("#ffffff");
|
g.setFont("6x8", 1).drawString(state.musicInfo.track, x, y + 22);
|
||||||
g.drawString(state.musicInfo.artist, x, y + 8);
|
}});
|
||||||
|
|
||||||
g.setFont("6x8", 1);
|
|
||||||
g.setColor("#ffffff");
|
|
||||||
g.drawString(state.musicInfo.track, x, y + 22);
|
|
||||||
}, changed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.music === "pause") {
|
if (state.music === "pause") {
|
||||||
hideNotification();
|
require("notify").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCallEvent(event) {
|
function handleCallEvent(event) {
|
||||||
|
|
||||||
if (event.cmd === "accept") {
|
if (event.cmd === "accept") {
|
||||||
showNotification(40, (y) => {
|
require("notify").show({
|
||||||
g.setColor("#ffffff");
|
size: 55, title: event.name,
|
||||||
g.drawImage(require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw=")), 8, y + 8);
|
body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))});
|
||||||
|
|
||||||
g.setFontAlign(-1, -1);
|
|
||||||
var x = 40;
|
|
||||||
g.setFont("4x6", 2);
|
|
||||||
g.setColor("#ffffff");
|
|
||||||
g.drawString(event.name, x, y + 8);
|
|
||||||
|
|
||||||
g.setFont("6x8", 1);
|
|
||||||
g.setColor("#ffffff");
|
|
||||||
g.drawString(event.number, x, y + 22);
|
|
||||||
});
|
|
||||||
|
|
||||||
Bangle.buzz();
|
Bangle.buzz();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,13 +77,6 @@
|
||||||
if(_GB)setTimeout(_GB,0,event);
|
if(_GB)setTimeout(_GB,0,event);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Touch control
|
|
||||||
Bangle.on("touch", () => {
|
|
||||||
if (state.scrollPos) {
|
|
||||||
hideNotification();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Bangle.on("swipe", (dir) => {
|
Bangle.on("swipe", (dir) => {
|
||||||
if (state.music === "play") {
|
if (state.music === "play") {
|
||||||
const command = dir > 0 ? "next" : "previous"
|
const command = dir > 0 ? "next" : "previous"
|
||||||
|
|
|
@ -55,7 +55,7 @@ const AppInfo = {
|
||||||
SET_TIME_ON_WRITE : false,
|
SET_TIME_ON_WRITE : false,
|
||||||
PRETOKENISE : options.settings.pretokenise,
|
PRETOKENISE : options.settings.pretokenise,
|
||||||
//MINIFICATION_LEVEL : "ESPRIMA", // disable due to https://github.com/espruino/BangleApps/pull/355#issuecomment-620124162
|
//MINIFICATION_LEVEL : "ESPRIMA", // disable due to https://github.com/espruino/BangleApps/pull/355#issuecomment-620124162
|
||||||
builtinModules : "Flash,Storage,heatshrink,tensorflow,locale"
|
builtinModules : "Flash,Storage,heatshrink,tensorflow,locale,notify"
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
return content;
|
return content;
|
||||||
|
|
42
js/index.js
42
js/index.js
|
@ -98,7 +98,9 @@ function handleCustomApp(appTemplate) {
|
||||||
});
|
});
|
||||||
console.log("Received custom app", app);
|
console.log("Received custom app", app);
|
||||||
modal.remove();
|
modal.remove();
|
||||||
Comms.uploadApp(app).then(()=>{
|
checkDependencies(app)
|
||||||
|
.then(()=>Comms.uploadApp(app))
|
||||||
|
.then(()=>{
|
||||||
Progress.hide({sticky:true});
|
Progress.hide({sticky:true});
|
||||||
resolve();
|
resolve();
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
|
@ -341,7 +343,9 @@ function uploadApp(app) {
|
||||||
if (appsInstalled.some(i => i.id === app.id)) {
|
if (appsInstalled.some(i => i.id === app.id)) {
|
||||||
return updateApp(app);
|
return updateApp(app);
|
||||||
}
|
}
|
||||||
Comms.uploadApp(app).then((appJSON) => {
|
checkDependencies(app)
|
||||||
|
.then(()=>Comms.uploadApp(app))
|
||||||
|
.then((appJSON) => {
|
||||||
Progress.hide({ sticky: true });
|
Progress.hide({ sticky: true });
|
||||||
if (appJSON) {
|
if (appJSON) {
|
||||||
appsInstalled.push(appJSON);
|
appsInstalled.push(appJSON);
|
||||||
|
@ -388,6 +392,31 @@ function customApp(app) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// check for dependencies the app needs and install them if required
|
||||||
|
function checkDependencies(app, uploadOptions) {
|
||||||
|
var promise = Promise.resolve();
|
||||||
|
if (app.dependencies) {
|
||||||
|
Object.keys(app.dependencies).forEach(dependency=>{
|
||||||
|
if (app.dependencies[dependency]!="type")
|
||||||
|
throw new Error("Only supporting dependencies on app types right now");
|
||||||
|
console.log(`Searching for dependency on app type '${dependency}'`);
|
||||||
|
var found = appsInstalled.find(app=>app.type==dependency);
|
||||||
|
if (found)
|
||||||
|
console.log(`Found dependency in installed app '${found.id}'`);
|
||||||
|
else {
|
||||||
|
found = appJSON.find(app=>app.type==dependency);
|
||||||
|
if (!found) throw new Error(`Dependency of '${dependency}' listed, but nothing satisfies it!`);
|
||||||
|
console.log(`Dependency not installed. Installing app id '${found.id}'`);
|
||||||
|
promise.then(new Promise((resolve,reject)=>{
|
||||||
|
console.log(`Install dependency '${dependency}':'${found.id}'`);
|
||||||
|
return Comms.uploadApp(found);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
function updateApp(app) {
|
function updateApp(app) {
|
||||||
if (app.custom) return customApp(app);
|
if (app.custom) return customApp(app);
|
||||||
return getInstalledApps().then(() => {
|
return getInstalledApps().then(() => {
|
||||||
|
@ -410,8 +439,9 @@ function updateApp(app) {
|
||||||
}).then(()=>{
|
}).then(()=>{
|
||||||
showToast(`Updating ${app.name}...`);
|
showToast(`Updating ${app.name}...`);
|
||||||
appsInstalled = appsInstalled.filter(a=>a.id!=app.id);
|
appsInstalled = appsInstalled.filter(a=>a.id!=app.id);
|
||||||
return Comms.uploadApp(app);
|
return checkDependencies(app);
|
||||||
}).then((appJSON) => {
|
}).then(()=>Comms.uploadApp(app)
|
||||||
|
).then((appJSON) => {
|
||||||
if (appJSON) appsInstalled.push(appJSON);
|
if (appJSON) appsInstalled.push(appJSON);
|
||||||
showToast(app.name+" Updated!", "success");
|
showToast(app.name+" Updated!", "success");
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
|
@ -549,7 +579,9 @@ function installMultipleApps(appIds, promptName) {
|
||||||
let app = apps.shift();
|
let app = apps.shift();
|
||||||
if (app===undefined) return resolve();
|
if (app===undefined) return resolve();
|
||||||
Progress.show({title:`${app.name} (${appCount-apps.length}/${appCount})`,sticky:true});
|
Progress.show({title:`${app.name} (${appCount-apps.length}/${appCount})`,sticky:true});
|
||||||
Comms.uploadApp(app,"skip_reset").then((appJSON) => {
|
checkDependencies(app,"skip_reset")
|
||||||
|
.then(()=>Comms.uploadApp(app,"skip_reset"))
|
||||||
|
.then((appJSON) => {
|
||||||
Progress.hide({sticky:true});
|
Progress.hide({sticky:true});
|
||||||
if (appJSON) appsInstalled.push(appJSON);
|
if (appJSON) appsInstalled.push(appJSON);
|
||||||
showToast(`(${appCount-apps.length}/${appCount}) ${app.name} Uploaded`);
|
showToast(`(${appCount-apps.length}/${appCount}) ${app.name} Uploaded`);
|
||||||
|
|
Loading…
Reference in New Issue