mirror of https://github.com/espruino/BangleApps
support for 'supports:["DEVICEID"]` for files in apps, merge launchb2 and launch
parent
375cbf3166
commit
eefa209af4
|
@ -262,6 +262,9 @@ and which gives information about the app for the Launcher.
|
||||||
// (eg it's evaluated as JS)
|
// (eg it's evaluated as JS)
|
||||||
"noOverwrite":true // if supplied, this file will not be overwritten if it
|
"noOverwrite":true // if supplied, this file will not be overwritten if it
|
||||||
// already exists
|
// already exists
|
||||||
|
"supports": ["BANGLEJS2"]// if supplied, this file will ONLY be uploaded to the device
|
||||||
|
// types named in the array. This allows different versions of
|
||||||
|
// the app to be uploaded for different platforms
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"data": [ // list of files the app writes to
|
"data": [ // list of files the app writes to
|
||||||
|
|
26
apps.json
26
apps.json
|
@ -94,31 +94,17 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "launch",
|
"id": "launch",
|
||||||
"name": "Launcher (Bangle.js 1 default)",
|
"name": "Launcher",
|
||||||
"shortName": "Launcher",
|
"shortName": "Launcher",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "This is needed by Bangle.js 1.0 to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
|
"description": "This is needed to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "launch",
|
"type": "launch",
|
||||||
"tags": "tool,system,launcher",
|
"tags": "tool,system,launcher",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"launch.app.js","url":"app.js"}
|
{"name":"launch.app.js","url":"app-bangle1.js","supports":["BANGLEJS"]},
|
||||||
],
|
{"name":"launch.app.js","url":"app-bangle2.js","supports":["BANGLEJS2"]}
|
||||||
"sortorder": -10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "launchb2",
|
|
||||||
"name": "Launcher (Bangle.js 2 default)",
|
|
||||||
"shortName": "Launcher",
|
|
||||||
"version": "0.04",
|
|
||||||
"description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications.",
|
|
||||||
"icon": "app.png",
|
|
||||||
"type": "launch",
|
|
||||||
"tags": "tool,system,launcher",
|
|
||||||
"supports": ["BANGLEJS2"],
|
|
||||||
"storage": [
|
|
||||||
{"name":"launchb2.app.js","url":"app.js"}
|
|
||||||
],
|
],
|
||||||
"sortorder": -10
|
"sortorder": -10
|
||||||
},
|
},
|
||||||
|
@ -4114,7 +4100,7 @@
|
||||||
"description": "A touch based stop watch for Bangle JS 2",
|
"description": "A touch based stop watch for Bangle JS 2",
|
||||||
"icon": "stopwatch.png",
|
"icon": "stopwatch.png",
|
||||||
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}],
|
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}],
|
||||||
"tags": "tools,app,b2",
|
"tags": "tools,app",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
|
@ -5,3 +5,4 @@
|
||||||
0.05: Use g.theme for colours
|
0.05: Use g.theme for colours
|
||||||
0.06: Use Bangle.setUI for buttons
|
0.06: Use Bangle.setUI for buttons
|
||||||
0.07: Theme colours fix
|
0.07: Theme colours fix
|
||||||
|
0.08: Merge Bangle.js 1 and 2 launchers
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
0.01: New App!
|
|
||||||
0.02: Fix occasional missed image when scrolling up
|
|
||||||
0.03: Text wrapping, better font
|
|
||||||
0.04: Reduce code duplication and use new E.showScroller
|
|
Binary file not shown.
Before Width: | Height: | Size: 899 B |
|
@ -12,6 +12,7 @@ var ROOTDIR = path.join(__dirname, '..');
|
||||||
var APPDIR = ROOTDIR+'/apps';
|
var APPDIR = ROOTDIR+'/apps';
|
||||||
var APPJSON = ROOTDIR+'/apps.json';
|
var APPJSON = ROOTDIR+'/apps.json';
|
||||||
var OUTFILE = ROOTDIR+'/firmware.js';
|
var OUTFILE = ROOTDIR+'/firmware.js';
|
||||||
|
var DEVICE = "BANGLEJS";
|
||||||
var APPS = [ // IDs of apps to install
|
var APPS = [ // IDs of apps to install
|
||||||
"boot","launch","mclock","setting",
|
"boot","launch","mclock","setting",
|
||||||
"about","alarm","widbat","widbt","welcome"
|
"about","alarm","widbat","widbt","welcome"
|
||||||
|
@ -61,7 +62,8 @@ Promise.all(APPS.map(appid => {
|
||||||
if (app===undefined) throw new Error(`App ${appid} not found`);
|
if (app===undefined) throw new Error(`App ${appid} not found`);
|
||||||
return AppInfo.getFiles(app, {
|
return AppInfo.getFiles(app, {
|
||||||
fileGetter : fileGetter,
|
fileGetter : fileGetter,
|
||||||
settings : SETTINGS
|
settings : SETTINGS,
|
||||||
|
device : { id : DEVICE }
|
||||||
}).then(files => {
|
}).then(files => {
|
||||||
appfiles = appfiles.concat(files);
|
appfiles = appfiles.concat(files);
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,7 +29,7 @@ if (DEVICE=="BANGLEJS") {
|
||||||
} else if (DEVICE=="BANGLEJS2") {
|
} else if (DEVICE=="BANGLEJS2") {
|
||||||
var OUTFILE = path.join(ROOTDIR, '../Espruino/libs/banglejs/banglejs2_storage_default.c');
|
var OUTFILE = path.join(ROOTDIR, '../Espruino/libs/banglejs/banglejs2_storage_default.c');
|
||||||
var APPS = [ // IDs of apps to install
|
var APPS = [ // IDs of apps to install
|
||||||
"boot","launchb2","s7clk","setting",
|
"boot","launch","s7clk","setting",
|
||||||
"about","alarm","widlock","widbat","widbt","widid"
|
"about","alarm","widlock","widbat","widbt","widid"
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,7 +132,8 @@ Promise.all(APPS.map(appid => {
|
||||||
if (app===undefined) throw new Error(`App ${appid} not found`);
|
if (app===undefined) throw new Error(`App ${appid} not found`);
|
||||||
return AppInfo.getFiles(app, {
|
return AppInfo.getFiles(app, {
|
||||||
fileGetter : fileGetter,
|
fileGetter : fileGetter,
|
||||||
settings : SETTINGS
|
settings : SETTINGS,
|
||||||
|
device : { id : DEVICE }
|
||||||
}).then(files => {
|
}).then(files => {
|
||||||
appfiles = appfiles.concat(files);
|
appfiles = appfiles.concat(files);
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,12 +50,12 @@ try{
|
||||||
}
|
}
|
||||||
|
|
||||||
const APP_KEYS = [
|
const APP_KEYS = [
|
||||||
'id', 'name', 'shortName', 'version', 'icon', 'description', 'tags', 'type',
|
'id', 'name', 'shortName', 'version', 'icon', 'screenshots', 'description', 'tags', 'type',
|
||||||
'sortorder', 'readme', 'custom', 'customConnect', 'interface', 'storage', 'data',
|
'sortorder', 'readme', 'custom', 'customConnect', 'interface', 'storage', 'data',
|
||||||
'supports', 'allow_emulator',
|
'supports', 'allow_emulator',
|
||||||
'dependencies'
|
'dependencies'
|
||||||
];
|
];
|
||||||
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite'];
|
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports'];
|
||||||
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
|
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
|
||||||
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
|
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
|
||||||
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
|
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
|
||||||
|
@ -107,6 +107,13 @@ apps.forEach((app,appIdx) => {
|
||||||
if (!app.description) ERROR(`App ${app.id} has no description`);
|
if (!app.description) ERROR(`App ${app.id} has no description`);
|
||||||
if (!app.icon) ERROR(`App ${app.id} has no icon`);
|
if (!app.icon) ERROR(`App ${app.id} has no icon`);
|
||||||
if (!fs.existsSync(appDir+app.icon)) ERROR(`App ${app.id} icon doesn't exist`);
|
if (!fs.existsSync(appDir+app.icon)) ERROR(`App ${app.id} icon doesn't exist`);
|
||||||
|
if (app.screenshots) {
|
||||||
|
if (!Array.isArray(app.screenshots)) ERROR(`App ${app.id} screenshots is not an array`);
|
||||||
|
app.screenshots.forEach(screenshot => {
|
||||||
|
if (!fs.existsSync(appDir+screenshot.url))
|
||||||
|
ERROR(`App ${app.id} screenshot file ${screenshot.url} not found`);
|
||||||
|
});
|
||||||
|
}
|
||||||
if (app.readme && !fs.existsSync(appDir+app.readme)) ERROR(`App ${app.id} README file doesn't exist`);
|
if (app.readme && !fs.existsSync(appDir+app.readme)) ERROR(`App ${app.id} README file doesn't exist`);
|
||||||
if (app.custom && !fs.existsSync(appDir+app.custom)) ERROR(`App ${app.id} custom HTML doesn't exist`);
|
if (app.custom && !fs.existsSync(appDir+app.custom)) ERROR(`App ${app.id} custom HTML doesn't exist`);
|
||||||
if (app.customConnect && !app.custom) ERROR(`App ${app.id} has customConnect but no customn HTML`);
|
if (app.customConnect && !app.custom) ERROR(`App ${app.id} has customConnect but no customn HTML`);
|
||||||
|
@ -128,13 +135,14 @@ apps.forEach((app,appIdx) => {
|
||||||
if (char) ERROR(`App ${app.id} storage file ${file.name} contains invalid character "${char[0]}"`)
|
if (char) ERROR(`App ${app.id} storage file ${file.name} contains invalid character "${char[0]}"`)
|
||||||
if (fileNames.includes(file.name))
|
if (fileNames.includes(file.name))
|
||||||
ERROR(`App ${app.id} file ${file.name} is a duplicate`);
|
ERROR(`App ${app.id} file ${file.name} is a duplicate`);
|
||||||
fileNames.push(file.name);
|
if (!file.supports) fileNames.push(file.name); // assume that there aren't duplicates if 'supports' is set
|
||||||
allFiles.push({app: app.id, file: file.name});
|
allFiles.push({app: app.id, file: file.name});
|
||||||
if (file.url) if (!fs.existsSync(appDir+file.url)) ERROR(`App ${app.id} file ${file.url} doesn't exist`);
|
if (file.url) if (!fs.existsSync(appDir+file.url)) ERROR(`App ${app.id} file ${file.url} doesn't exist`);
|
||||||
if (!file.url && !file.content && !app.custom) ERROR(`App ${app.id} file ${file.name} has no contents`);
|
if (!file.url && !file.content && !app.custom) ERROR(`App ${app.id} file ${file.name} has no contents`);
|
||||||
var fileContents = "";
|
var fileContents = "";
|
||||||
if (file.content) fileContents = file.content;
|
if (file.content) fileContents = file.content;
|
||||||
if (file.url) fileContents = fs.readFileSync(appDir+file.url).toString();
|
if (file.url) fileContents = fs.readFileSync(appDir+file.url).toString();
|
||||||
|
if (file.supports && !Array.isArray(file.supports)) ERROR(`App ${app.id} file ${file.name} supports field is not an array`);
|
||||||
if (file.evaluate) {
|
if (file.evaluate) {
|
||||||
try {
|
try {
|
||||||
acorn.parse("("+fileContents+")");
|
acorn.parse("("+fileContents+")");
|
||||||
|
@ -165,7 +173,7 @@ apps.forEach((app,appIdx) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key in file) {
|
for (const key in file) {
|
||||||
if (!STORAGE_KEYS.includes(key)) ERROR(`App ${app.id}'s ${file.name} has unknown key ${key}`);
|
if (!STORAGE_KEYS.includes(key)) ERROR(`App ${app.id} file ${file.name} has unknown key ${key}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let dataNames = [];
|
let dataNames = [];
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#!/usr/bin/node
|
#!/usr/bin/node
|
||||||
|
|
||||||
|
/*
|
||||||
|
var EMULATOR = "banglejs2";
|
||||||
|
var DEVICEID = "BANGLEJS2";
|
||||||
|
*/
|
||||||
var EMULATOR = "banglejs1";
|
var EMULATOR = "banglejs1";
|
||||||
|
var DEVICEID = "BANGLEJS";
|
||||||
|
|
||||||
var singleAppId;
|
var singleAppId;
|
||||||
|
|
||||||
|
@ -40,6 +45,10 @@ var apps = JSON.parse(require("fs").readFileSync(__dirname+"/../apps.json"));
|
||||||
/* we factory reset ONCE, get this, then we can use it to reset
|
/* we factory reset ONCE, get this, then we can use it to reset
|
||||||
state quickly for each new app */
|
state quickly for each new app */
|
||||||
var factoryFlashMemory = new Uint8Array(FLASH_SIZE);
|
var factoryFlashMemory = new Uint8Array(FLASH_SIZE);
|
||||||
|
// Log of messages from app
|
||||||
|
var appLog = "";
|
||||||
|
// List of apps that errored
|
||||||
|
var erroredApps = [];
|
||||||
|
|
||||||
jsRXCallback = function() {};
|
jsRXCallback = function() {};
|
||||||
jsUpdateGfx = function() {};
|
jsUpdateGfx = function() {};
|
||||||
|
@ -49,6 +58,10 @@ function ERROR(s) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onConsoleOutput(txt) {
|
||||||
|
appLog += txt + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
function getThumbnail(appId, imageFn) {
|
function getThumbnail(appId, imageFn) {
|
||||||
console.log("Thumbnail for "+appId);
|
console.log("Thumbnail for "+appId);
|
||||||
var app = apps.find(a=>a.id==appId);
|
var app = apps.find(a=>a.id==appId);
|
||||||
|
@ -61,7 +74,10 @@ function getThumbnail(appId, imageFn) {
|
||||||
fileGetter:function(url) {
|
fileGetter:function(url) {
|
||||||
console.log(__dirname+"/"+url);
|
console.log(__dirname+"/"+url);
|
||||||
return Promise.resolve(require("fs").readFileSync(__dirname+"/../"+url).toString("binary"));
|
return Promise.resolve(require("fs").readFileSync(__dirname+"/../"+url).toString("binary"));
|
||||||
}, settings : SETTINGS}).then(files => {
|
},
|
||||||
|
settings : SETTINGS,
|
||||||
|
device : { id : DEVICEID }
|
||||||
|
}).then(files => {
|
||||||
console.log("AppInfo returned");//, files);
|
console.log("AppInfo returned");//, files);
|
||||||
flashMemory.set(factoryFlashMemory);
|
flashMemory.set(factoryFlashMemory);
|
||||||
jsTransmitString("reset()\n");
|
jsTransmitString("reset()\n");
|
||||||
|
@ -69,6 +85,7 @@ function getThumbnail(appId, imageFn) {
|
||||||
jsTransmitString("g.clear()\n");
|
jsTransmitString("g.clear()\n");
|
||||||
var command = files.map(f=>f.cmd).join("\n")+"\n";
|
var command = files.map(f=>f.cmd).join("\n")+"\n";
|
||||||
command += `load("${appId}.app.js")\n`;
|
command += `load("${appId}.app.js")\n`;
|
||||||
|
appLog = "";
|
||||||
jsTransmitString(command);
|
jsTransmitString(command);
|
||||||
console.log("Done.");
|
console.log("Done.");
|
||||||
jsStopIdle();
|
jsStopIdle();
|
||||||
|
@ -79,6 +96,9 @@ function getThumbnail(appId, imageFn) {
|
||||||
var firstPixel = rgba32[0];
|
var firstPixel = rgba32[0];
|
||||||
var blankImage = rgba32.every(col=>col==firstPixel)
|
var blankImage = rgba32.every(col=>col==firstPixel)
|
||||||
|
|
||||||
|
if (appLog.indexOf("Uncaught")>=0)
|
||||||
|
erroredApps.push( { id : app.id, log : appLog } );
|
||||||
|
|
||||||
if (!blankImage) {
|
if (!blankImage) {
|
||||||
var Jimp = require("jimp");
|
var Jimp = require("jimp");
|
||||||
let image = new Jimp(GFX_WIDTH, GFX_HEIGHT, function (err, image) {
|
let image = new Jimp(GFX_WIDTH, GFX_HEIGHT, function (err, image) {
|
||||||
|
@ -113,20 +133,22 @@ setTimeout(function() {
|
||||||
console.log("Ready!");
|
console.log("Ready!");
|
||||||
|
|
||||||
if (singleAppId) {
|
if (singleAppId) {
|
||||||
getThumbnail(singleAppId, "screenshots/"+singleAppId+".png")
|
getThumbnail(singleAppId, "screenshots/"+singleAppId+"-"+EMULATOR+".png");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var appList = apps.filter(app => (!app.type || app.type=="clock") && !app.custom).map(app=>app.id);
|
var appList = apps.filter(app => (!app.type || app.type=="clock") && !app.custom);
|
||||||
// TODO: Work out about Bangle.js 1 or 2
|
appList = appList.filter(app => !app.screenshots && app.supports.includes(DEVICEID));
|
||||||
|
|
||||||
var promise = Promise.resolve();
|
var promise = Promise.resolve();
|
||||||
appList.forEach(appId => {
|
appList.forEach(app => {
|
||||||
promise = promise.then(() => {
|
promise = promise.then(() => {
|
||||||
return getThumbnail(appId, "screenshots/"+appId+".png").then(ok => {
|
var imageFile = "screenshots/"+app.id+"-"+EMULATOR+".png";
|
||||||
|
return getThumbnail(app.id, imageFile).then(ok => {
|
||||||
screenshots.push({
|
screenshots.push({
|
||||||
id : appId,
|
id : app.id,
|
||||||
url : "screenshots/"+appId+".png"
|
url : imageFile,
|
||||||
|
version: app.version
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -135,6 +157,7 @@ setTimeout(function() {
|
||||||
promise.then(function() {
|
promise.then(function() {
|
||||||
console.log("Complete!");
|
console.log("Complete!");
|
||||||
require("fs").writeFileSync("screenshots.json", JSON.stringify(screenshots,null,2));
|
require("fs").writeFileSync("screenshots.json", JSON.stringify(screenshots,null,2));
|
||||||
|
console.log("Errored Apps", erroredApps);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
2
core
2
core
|
@ -1 +1 @@
|
||||||
Subproject commit a7d82825d3a43e1da7919591ed6fa776f1f0545a
|
Subproject commit 70b49d8dbd2afa76f4485aadf679dc75e0a8b4ac
|
Loading…
Reference in New Issue