Anwendungen bestehen konventionsgemäß aus einer Reihe von Dateien, die einem bestimmten Namensschema folgen: wenn "AppName" der Name einer Anwendung ist, dann enthalten
+AppName
einen Deskriptor mit Informationen zur Anwendung,*AppName
ein Icon für die Anwendung,-AppName
den eigentlichen Quelltext der Anwendung,@AppName
zusätzliche Daten für die Anwendung und=AppName
Anweisungen, die nach einem Neustart der Uhr ausgeführt werden sollenDer "Deskriptor" einer Anwendung ist ein JSON-Objekt mit einer Reihe von Feldern vorgegebener Bedeutung:
name
- voller Name der Anwendungtype
- "app", "clock" oder "widget" (Voreinstellung: "app")icon
- Name der Datei mit dem Icon der Anwendungsrc
- Name der Datei mit dem Quelltext der Anwendungsortorder
- beeinflusst die Sortierung aller Anwendungen im Menü der Uhrversion
- Versionsnummer der Anwendungfiles
- Komma-separierte Liste aller zur Anwendung gehörenden DateienAuf der Uhr des Autors sieht der Deskriptor für die "Analog Clock" beispielsweise wie folgt aus:
{
"name": "Analog Clock",
"type": "clock",
"icon": "*aclock",
"src": "-aclock",
"sortorder":-10,
"version": "0.01",
"files": "+aclock,-aclock,*aclock"
}
Bangle.js-typisch ist das "Icon" für eine Anwendung nicht etwa eine Bilddatei sondern eine JavaScript-Anweisung, die ein Image-Objekt für ein Icon erzeugt.
Die erforderlichen Rohdaten dafür können mit dem Espruino Image Converter erzeugt werden. Dazu konvertiert man eine Rastergrafik der Größe 48x48 Pixel mit folgenden Einstellungen:
und schreibt die Konverter-Ausgabe in die Icon-Datei.
Wünscht man eine Liste aller installierten Anwendungen, so entspricht diese folglich der Liste all der Dateien auf dem Dateisystem der Uhr, die mit einem "+" beginnen.
auf Uhr ausführenEine Anwendung zu installieren bedeutet also, eine Reihe von Dateien auf das Dateisystem einer Bangle.js zu schreiben - dies kann auch von der Espruino IDE aus geschehen.
Der Deskriptor für eine Anwendung kann wie folgt angelegt werden:
const Storage = require('Storage');
let AppName = 'HelWrld';
Storage.write('+' + AppName, JSON.stringify({
name: '"Hello, World!" for Bangle.js',
type: 'app',
icon: '*' + AppName,
src: '-' + AppName,
version: '0.0.1',
files: '+'+AppName+',-'+AppName+',*'+AppName
}));
Die letzte Zeile des Deskriptors deutet bereits an, daß wir vorhaben, neben der eigentlichen Anwendung selbst auch noch ein Icon dafür bereitzustellen.
Für das Icon einer Anwendung lässt man eine Rastergrafik in der Größe 48x48 Pixel wie eingangs beschrieben vom Image Converter in ein Bangle.js-konformes Format bringen und schreibt den Teil hinter "var img = " aus der Konverter-Ausgabe in folgendes Skript:
const Storage = require('Storage');
let AppName = 'HelWrld';
Storage.write(
'*' + AppName,
'require("heatshrink").decompress(atob("mEwxH+If4A/AH4A/AH4A/AH4A/AH4A3lQDGBpIPLACEw1EwAYkGBpAJBAoYvY1wvCAYQEBEYYJDF4QPDCIwvRqkqqgfCqmEwg0DF5BlDMyYbBAAcqlWurVaNIxfGFwNUqgwSL4YpBF4Wu1AvEdQIvDeAQEBAYIRCDIK/TCoOorVUKgr1EAoRfGfZ4PDAYYbBwhJDYwIeDAogRBGwbbDABglDAYcwmCIBAwZFFAghwDwhwDAFTxQKoZZEAH4AWX44TPOwYbSmGoUIIDDSRYPDGwgbGF5muB4NUAYQIBC4ZMBAwI5BCYYMDrQHCBYwvKqmEF4dU1GoqgMBwla1GuGYQPCGoIXB1wvTEQIBClR6BqgxBAoIhBqkqL4yIBCIIvSIwQuBLIWuBIJZBF4IJBOYjkCCIgvCwgvOSAQWDL4z4EBgRfIlVUCASQNMIJCCX4uoJgoRGX4hfPIIMGPoQ3CC4YJEBgYRFBwYuPAH4A/AH4A/AH4A/AH4A/AH4A/AC4A="))'
);
Für das Beispiel wurde im Emulator ein Bildschirmabzug von einem kleinen Skript genommen, zurecht geschnitten und im Espruino Image Converter passend konvertiert.
im Emulator ausführen auf Uhr ausführenFür die Anwendung selbst wird deren vollständiger Quelltext (evtl. nach einer vorherigen Minifizierung) ebenfalls in eine Datei geschrieben:
const Storage = require('Storage');
let AppName = 'HelWrld';
Storage.write('-' + AppName, `
Bangle.setLCDMode('80x80');
g.clear();
g.setFont('6x8',2);
g.setColor('#BFFF00');
/**** draw actual icon contents ****/
g.drawString('Hello,', 6,20);
g.drawString('World!', 6,40);
g.flip();
`);
Damit ist die Anwendung vollständig installiert.
Eine Anwendung zu deinstallieren bedeutet, alle zu dieser Anwendung gehörenden Dateien zu löschen.
Da eine Anwendung über einen Deskriptor verfügen muss, kann man über den Eintrag files
aus diesem Deskriptor in Erfahrung bringen, welche Dateien das sind.
Alternativ kann man allerdings auch gleich alle Dateien mit den Namen
"+AppName", "*AppName", "-AppName", "@AppName", "=AppName"
löschen - da die Operation idempotent ist, stellt das Löschen einer nicht vorhandenen Datei keinen Fehler dar.
const Storage = require('Storage');
let AppName = 'HelWrld';
(function () {
try {
let FileList = Storage.readJSON(AppName);['files'].split(',');
for (let i = 0, l = FileList.length; i < l; i++) {
Storage.erase(FileList[i]);
}
return;
} catch (Signal) { /* nop */ }
Storage.erase('+' + AppName);
Storage.erase('*' + AppName);
Storage.erase('-' + AppName);
Storage.erase('@' + AppName);
Storage.erase('=' + AppName);
})();