2022-05-23 15:54:00 +00:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
2024-02-09 02:58:42 +00:00
< meta name = "viewport" content = "width=device-width, initial-scale=0.8" >
2022-05-23 15:54:00 +00:00
< link rel = "stylesheet" href = "css/spectre.min.css" >
< link rel = "stylesheet" href = "css/spectre-exp.min.css" >
< link rel = "stylesheet" href = "css/spectre-icons.min.css" >
< link rel = "stylesheet" href = "css/pwa.css" >
< link rel = "stylesheet" href = "css/main.css" >
< link rel = "apple-touch-icon" sizes = "180x180" href = "img/apple-touch-icon.png" >
< link rel = "icon" type = "image/png" sizes = "32x32" href = "img/favicon-32x32.png" >
< link rel = "icon" type = "image/png" sizes = "16x16" href = "img/favicon-16x16.png" >
< link rel = "manifest" href = "site.webmanifest" >
< link rel = "mask-icon" href = "img/safari-pinned-tab.svg" color = "#5755d9" >
< meta name = "apple-mobile-web-app-title" content = "BangleApps" >
< meta name = "application-name" content = "BangleApps" >
< meta name = "msapplication-TileColor" content = "#5755d9" >
< meta name = "theme-color" content = "#5755d9" >
< title > Bangle.js App Loader< / title >
< / head >
< body >
< header class = "navbar-primary navbar" >
< section class = "navbar-section" >
< a href = "https://banglejs.com" target = "_blank" class = "navbar-brand mr-2" > < img src = "img/banglejs-logo-sml.png" alt = "Bangle.js" >
< div > App Loader< / div > < / a >
<!-- <a href="#" class="btn btn - link">...</a> -->
< / section >
< section class = "navbar-section" >
< button class = "btn" id = "connectmydevice" > Connect< / button >
< / section >
<!-- <section class="navbar - section">
< div class = "input-group input-inline" >
< input class = "form-input" type = "text" placeholder = "search" >
< button class = "btn btn-primary input-group-btn" > Search< / button >
< / div >
< / section > -->
< / header >
< div class = "container" style = "padding-top:4px" >
< p id = "requireHTTPS" class = "hidden" >
< b > STOP!< / b > This page < b > must< / b > be served over HTTPS. Please < a > reload this page via HTTPS< / a > .
< / p >
< / div >
< ul class = "tab tab-block" id = "tab-navigate" >
< li class = "tab-item active" id = "tab-librarycontainer" >
< a href = "javascript:showTab('librarycontainer')" > Library< / a >
< / li >
< li class = "tab-item" id = "tab-myappscontainer" >
< a href = "javascript:showTab('myappscontainer')" > My Apps< / a >
< / li >
< li class = "tab-item" id = "tab-morecontainer" >
< a href = "javascript:showTab('morecontainer')" > More...< / a >
< / li >
< / ul >
< div class = "container" id = "toastcontainer" >
< / div >
< div class = "container apploader-tab" id = "librarycontainer" >
< div class = "dropdown-container" >
< div class = "dropdown devicetype-nav" >
< a href = "#" class = "btn btn-link dropdown-toggle" tabindex = "0" >
< span > All apps< / span > < i class = "icon icon-caret" > < / i >
< / a >
<!-- menu component -->
< ul class = "menu" >
< li class = "menu-item" > < a > All apps< / a > < / li >
< li class = "menu-item" > < a dt = "BANGLEJS" > Bangle.js 1< / a > < / li >
< li class = "menu-item" > < a dt = "BANGLEJS2" > Bangle.js 2< / a > < / li >
< / ul >
< / div >
< div class = "filter-nav" >
2023-04-28 08:58:39 +00:00
< label class = "chip active" filterid = "" data-tooltip = "Show all apps" > All< / label >
< label class = "chip tooltip" filterid = "clock" data-tooltip = "To tell the time!" > Clocks< / label >
< label class = "chip tooltip" filterid = "launch" data-tooltip = "Choose which apps to launch" > Launchers< / label >
< label class = "chip tooltip" filterid = "game" data-tooltip = "Have fun!" > Games< / label >
< label class = "chip tooltip" filterid = "tool" data-tooltip = "Useful applications" > Tools< / label >
< label class = "chip tooltip" filterid = "textinput" data-tooltip = "To allow you to enter text" > Keyboards< / label >
< label class = "chip tooltip" filterid = "widget" data-tooltip = "Appear in the top bar of Bangle.js apps" > Widgets< / label >
< label class = "chip tooltip" filterid = "bluetooth" data-tooltip = "Using Bluetooth Functionality" > Bluetooth< / label >
< label class = "chip tooltip" filterid = "outdoors" data-tooltip = "For outdoor use" > Outdoors< / label >
< label class = "chip tooltip" filterid = "ram" data-tooltip = "Apps that don't save anything to flash memory" > Online< / label >
< label class = "chip tooltip" filterid = "clkinfo" data-tooltip = "Info displayed on clocks, or clocks with info" > Clock Info< / label >
2023-11-13 08:54:01 +00:00
< label class = "chip tooltip" filterid = "health" data-tooltip = "Apps for your health" > Health< / label >
2023-04-28 08:58:39 +00:00
< label class = "chip tooltip" filterid = "favourites" data-tooltip = "Apps that you've liked ❤️" > Favourites< / label >
2022-05-23 15:54:00 +00:00
< / div >
< div class = "sort-nav hidden" >
< span > Sort by:< / span >
< label class = "chip active" sortid = "" > None< / label >
2023-04-28 08:58:39 +00:00
< label class = "chip hidden tooltip" sortid = "created" data-tooltip = "Most recent apps" > New< / label >
< label class = "chip hidden tooltip" sortid = "modified" data-tooltip = "Most recently changed" > Updated< / label >
< label class = "chip hidden tooltip" sortid = "installs" data-tooltip = "Most installed by users" > Installed< / label >
< label class = "chip hidden tooltip" sortid = "favourites" data-tooltip = "Most liked by users" > Favourited< / label >
2022-05-23 15:54:00 +00:00
< / div >
< / div >
< div class = "panel" style = "clear:both" >
< div class = "panel-header" >
< div class = "input-group" id = "searchform" >
< input class = "form-input" type = "text" placeholder = "Keywords..." >
< button class = "btn btn-primary input-group-btn" > Search< / button >
< / div >
< / div >
< div class = "panel-body columns" > <!-- apps go here --> < / div >
< / div >
< / div >
< div class = "container apploader-tab" id = "myappscontainer" style = "display:none" >
< div class = "panel" >
< div class = "panel-header" style = "text-align:right" >
< button class = "btn refresh" > Refresh...< / button >
< button class = "btn btn-primary updateapps hidden" > Update X apps< / button >
< / div >
< div class = "panel-body columns" > <!-- apps go here --> < / div >
< / div >
< / div >
< div class = "container apploader-tab" id = "morecontainer" style = "display:none" >
< div class = "hero bg-gray" >
< div class = "hero-body" >
< a href = "https://banglejs.com" target = "_blank" > < img src = "img/banglejs-logo-mid.png" alt = "Bangle.js" > < / a >
< h2 > App Loader< / h2 >
< p > A tool for uploading and removing apps from < a href = "https://banglejs.com" target = "_blank" > Bangle.js Smart Watches< / a > < / p >
< / div >
< / div >
< div class = "container" style = "padding-top: 8px;" >
< p > < b > Can't connect?< / b > Check out the < a href = "https://www.espruino.com/Troubleshooting+Bangle.js" target = "_blank" > Bangle.js Troubleshooting page< / a >
< p id = "apploaderlinks" > < / p >
< p > Check out < a href = "https://github.com/espruino/BangleApps" target = "_blank" > the Source on GitHub< / a > , or
find out < a href = "https://www.espruino.com/Bangle.js+App+Loader" target = "_blank" > how to add your own app< / a > < / p >
< p > Using < a href = "https://espruino.com/" target = "_blank" > Espruino< / a > , Icons from < a href = "https://icons8.com/" target = "_blank" > icons8.com< / a > < / p >
< h3 > Utilities< / h3 >
2023-05-05 09:47:49 +00:00
< p >
< button class = "btn tooltip" id = "settime" data-tooltip = "Set the Bangle's time to your Browser's time" > Set Bangle.js Time< / button >
2023-07-23 12:24:38 +00:00
< button class = "btn tooltip" id = "screenshot" data-tooltip = "Create screenshot" > Screenshot< / button >
< button class = "btn tooltip" id = "downloadallapps" data-tooltip = "Download all Bangle.js files to a ZIP file" > Backup< / button >
< button class = "btn tooltip" id = "uploadallapps" data-tooltip = "Restore Bangle.js from a ZIP file" > Restore< / button >
< / p > < p >
2023-04-28 08:58:39 +00:00
< button class = "btn tooltip" id = "removeall" data-tooltip = "Delete everything, leave it blank" > Remove all Apps< / button >
< button class = "btn tooltip" id = "reinstallall" data-tooltip = "Re-install every app, leave all data" > Reinstall apps< / button >
< button class = "btn tooltip" id = "installdefault" data-tooltip = "Delete everything, install default apps" > Install default apps< / button >
< button class = "btn tooltip" id = "installfavourite" data-tooltip = "Delete everything, install your favourites" > Install favourite apps< / button >
2023-07-23 12:24:38 +00:00
< button class = "btn tooltip" id = "defaultbanglesettings" data-tooltip = "Reset your Bangle's settings to the defaults" > Reset Settings< / button >
2023-05-05 09:47:49 +00:00
< / p > < p >
< button class = "btn tooltip" id = "newGithubIssue" data-tooltip = "Create a new issue on GitHub" > New issue on GitHub< / button >
2023-05-05 10:15:40 +00:00
< button class = "btn tooltip" id = "webideremote" data-tooltip = "Enable the Web IDE remote server" > Web IDE Remote< / button >
2023-05-05 09:47:49 +00:00
< / p >
2022-05-23 15:54:00 +00:00
< h3 > Settings< / h3 >
< div class = "form-group" >
< label class = "form-switch" >
< input type = "checkbox" id = "settings-pretokenise" >
< i class = "form-icon" > < / i > Pretokenise apps before upload (smaller, faster apps)
< / label >
< label class = "form-switch" >
< input type = "checkbox" id = "settings-settime" >
< i class = "form-icon" > < / i > Always update time when we connect
< / label >
2023-04-28 07:17:15 +00:00
< label class = "form-switch" >
< input type = "checkbox" id = "settings-usage-stats" >
< i class = "form-icon" > < / i > Send app analytics to banglejs.com (apps installed, favourites, firmware version).< br / >
< small > Used for 'Sort by Installed/Favourited' functionality. See the < a href = "http://www.espruino.com/Privacy" > privacy policy< / a > < / small > .
< / label >
2022-05-23 15:54:00 +00:00
< div class = "form-group" >
< select class = "form-select form-inline" id = "settings-lang" style = "width: 10em" >
< option value = "" > None (English)< / option >
< / select > < span > Translations (< a href = "https://github.com/espruino/BangleApps/issues/1311" target = "_blank" > BETA - more info< / a > ). Any apps that are uploaded to Bangle.js after changing this will have any text automatically translated.< / span >
< / div >
2023-04-28 07:17:15 +00:00
< details >
< summary > Advanced Options< / summary >
< label class = "form-switch" >
< input type = "checkbox" id = "settings-minify" >
2023-04-28 08:58:39 +00:00
< i class = "form-icon" > < / i > Minify apps before upload (⚠️ DANGER⚠️ : Not recommended. Uploads smaller, faster apps but this < b > will< / b > break many apps)
2023-04-28 07:17:15 +00:00
< / label >
2023-07-23 12:00:47 +00:00
< label class = "form-switch" >
< input type = "checkbox" id = "settings-alwaysAllowUpdate" >
2023-10-16 09:21:48 +00:00
< i class = "form-icon" > < / i > Always show "reinstall app" button < i class = "icon icon-refresh" > < / i > regardless of the version
2023-07-23 12:00:47 +00:00
< / label >
2023-10-16 09:21:48 +00:00
< label class = "form-switch" >
< input type = "checkbox" id = "settings-autoReload" >
< i class = "form-icon" > < / i > Automatically reload watch after app App Loader actions (removes "Hold button" prompt)
< / label >
< button class = "btn" id = "defaultsettings" > Reset App Loader settings to defaults< / button >
2023-04-28 07:17:15 +00:00
< / details >
2022-05-23 15:54:00 +00:00
< / div >
< div id = "more-deviceinfo" style = "display:none" >
< h3 > Device info< / h3 >
< div id = "more-deviceinfo-content" > < / div >
< / div >
< / div >
< / div >
< footer class = "floating hidden" >
<!-- Install button, hidden by default -->
< div id = "installContainer" class = "hidden" >
< button id = "butInstall" type = "button" >
Install
< / button >
< / div >
< / footer >
2022-11-21 12:16:27 +00:00
< script src = "webtools/puck.js" > < / script >
< script src = "webtools/heatshrink.js" > < / script >
2022-05-23 15:54:00 +00:00
< script src = "core/lib/marked.min.js" > < / script >
< script src = "core/lib/espruinotools.js" > < / script >
< script src = "core/js/utils.js" > < / script >
< script src = "loader.js" > < / script >
< script src = "https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js" > < / script > <!-- for backup.js -->
< script src = "backup.js" > < / script >
< script src = "core/js/ui.js" > < / script >
< script src = "core/js/comms.js" > < / script >
< script src = "core/js/appinfo.js" > < / script >
< script src = "core/js/index.js" > < / script >
< script src = "core/js/pwa.js" defer > < / script >
2023-05-05 10:15:40 +00:00
<!-- FIXME - use espruino.com/ide, github -->
< script src = "https://espruino.github.io/EspruinoWebIDE/js/libs/peerjs.min.js" > < / script >
< script src = "https://espruino.github.io/EspruinoWebIDE/EspruinoTools/libs/webrtc-connection.js" > < / script >
2022-05-23 15:54:00 +00:00
< script >
/*Android = {
bangleTx : function(data) {
console.log("TX : "+JSON.stringify(data));
}
};*/
/*document.getElementById("test").addEventListener("click", function() {
console.log("Pressed");
Android.bangleTx("LED1.toggle();\n");
});*/
if (typeof Android!=="undefined") {
console.log("Running in Android, overwrite Puck library");
var isBusy = false;
var queue = [];
var connection = {
cb : function(data) {},
write : function(data, writecb) {
Android.bangleTx(data);
Puck.writeProgress(data.length, data.length);
if (writecb) setTimeout(writecb,10);
},
close : function() {},
2023-05-05 11:52:45 +00:00
on : function(evt,cb) { connection.handlers[evt] = cb; },
2022-05-23 15:54:00 +00:00
received : "",
2023-05-05 10:15:40 +00:00
hadData : false,
handlers : []
2022-05-23 15:54:00 +00:00
}
2024-01-11 10:06:38 +00:00
connection.on("data", function(d) {
connection.received += d;
connection.hadData = true;
if (connection.cb) connection.cb(d);
});
2022-05-23 15:54:00 +00:00
function bangleRx(data) {
// document.getElementById("status").innerText = "RX:"+data;
2023-05-05 10:15:40 +00:00
// call data event
if (connection.handlers["data"])
connection.handlers["data"](data);
2022-05-23 15:54:00 +00:00
}
function log(level, s) {
if (Puck.log) Puck.log(level, s);
}
function handleQueue() {
if (!queue.length) return;
var q = queue.shift();
log(3,"Executing "+JSON.stringify(q)+" from queue");
if (q.type == "write") Puck.write(q.data, q.callback, q.callbackNewline);
else log(1,"Unknown queue item "+JSON.stringify(q));
}
/* convenience function... Write data, call the callback with data:
callbackNewline = false => if no new data received for ~0.2 sec
callbackNewline = true => after a newline */
function write(data, callback, callbackNewline) {
let result;
/// If there wasn't a callback function, then promisify
if (typeof callback !== 'function') {
callbackNewline = callback;
result = new Promise((resolve, reject) => callback = (value, err) => {
if (err) reject(err);
else resolve(value);
});
}
if (isBusy) {
log(3, "Busy - adding Puck.write to queue");
queue.push({type:"write", data:data, callback:callback, callbackNewline:callbackNewline});
return result;
}
var cbTimeout;
function onWritten() {
if (callbackNewline) {
connection.cb = function(d) {
var newLineIdx = connection.received.indexOf("\n");
if (newLineIdx>=0) {
var l = connection.received.substr(0,newLineIdx);
connection.received = connection.received.substr(newLineIdx+1);
connection.cb = undefined;
if (cbTimeout) clearTimeout(cbTimeout);
cbTimeout = undefined;
if (callback)
callback(l);
isBusy = false;
handleQueue();
}
};
}
// wait for any received data if we have a callback...
var maxTime = 300; // 30 sec - Max time we wait in total, even if getting data
var dataWaitTime = callbackNewline ? 100/*10 sec if waiting for newline*/ : 3/*300ms*/;
var maxDataTime = dataWaitTime; // max time we wait after having received data
cbTimeout = setTimeout(function timeout() {
cbTimeout = undefined;
if (maxTime) maxTime--;
if (maxDataTime) maxDataTime--;
if (connection.hadData) maxDataTime=dataWaitTime;
if (maxDataTime & & maxTime) {
cbTimeout = setTimeout(timeout, 100);
} else {
connection.cb = undefined;
if (callback)
callback(connection.received);
isBusy = false;
handleQueue();
connection.received = "";
}
connection.hadData = false;
}, 100);
}
if (!connection.txInProgress) connection.received = "";
isBusy = true;
connection.write(data, onWritten);
return result
}
// ----------------------------------------------------------
2023-05-05 10:15:40 +00:00
2022-05-23 15:54:00 +00:00
Puck = {
/// Are we writing debug information? 0 is no, 1 is some, 2 is more, 3 is all.
debug : Puck.debug,
/// Should we use flow control? Default is true
flowControl : true,
/// Used internally to write log information - you can replace this with your own function
log : function(level, s) { if (level < = this.debug) console.log("< BLE > "+s)},
/// Called with the current send progress or undefined when done - you can replace this with your own function
writeProgress : Puck.writeProgress,
connect : function(callback) {
2023-05-05 10:15:40 +00:00
setTimeout(callback, 10, connection);
return connection;
2022-05-23 15:54:00 +00:00
},
write : write,
eval : function(expr, cb) {
const response = write('\x10Bluetooth.println(JSON.stringify(' + expr + '))\n', true)
.then(function (d) {
try {
return JSON.parse(d);
} catch (e) {
log(1, "Unable to decode " + JSON.stringify(d) + ", got " + e.toString());
return Promise.reject(d);
}
});
if (cb) {
return void response.then(cb, (err) => cb(null, err));
} else {
return response;
}
},
isConnected : function() { return true; },
getConnection : function() { return connection; },
close : function() {
if (connection)
connection.close();
},
};
// no need for header
document.getElementsByTagName("header")[0].style="display:none";
// force connection attempt automatically
setTimeout(function() {
getInstalledApps(true).catch(err => {
showToast("Device connection failed, "+err,"error");
if ("object"==typeof err) console.log(err.stack);
});
}, 500);
2023-05-05 12:06:36 +00:00
} else {
showToast("You're running the App Loader version for Gadgetbridge, but you don't seem to be in Gadgetbridge!","error");
}
function showWebRTCID(id) {
showToast("Bridge's Peer ID: "+id);
showPrompt("Web IDE Remote Access",`
Remote access enabled. Peer ID:
< br / > < br / >
< b > ${id}< / b >
< br / > < br / >
Go to < b > espruino.com/ide< / b > on your
desktop and enter this code under
< b > Remote Connection Bridge Peer ID< / b > in Settings.
Then connect to the < b > Android< / b > device.
`,{ok:1},false/*shouldEscapeHtml*/).then(() => {
}, function() { /* cancelled */ });
2022-05-23 15:54:00 +00:00
}
2023-05-05 10:15:40 +00:00
// Button to Enable Remote Web IDE
var el = document.getElementById("webideremote");
2023-05-05 12:06:36 +00:00
var webrtc;
2023-05-05 10:15:40 +00:00
if (el) el.addEventListener("click", event=>{
2023-05-05 12:06:36 +00:00
if (webrtc) showWebRTCID(webrtc.peerId);
else {
webrtc = webrtcInit({
bridge:true,
onStatus : function(s) {
showToast(s);
2023-05-05 10:15:40 +00:00
},
2023-05-05 12:06:36 +00:00
onPeerID : function(id) {
showWebRTCID(id);
},
onGetPorts : function(cb) {
cb([{path:"Android",description:"Remote Device Connection",type:"socket"}]);
},
onPortConnect : function(serialPort, cb) {
cb(); // we're already connected...
},
onPortDisconnect : function(serialPort) {
},
onPortWrite : function(data, cb) {
Puck.write(data, cb);
}
});
connection.on("data", function(d) {
webrtc.onPortReceived(d);
});
}
2023-05-05 10:15:40 +00:00
});
2022-05-23 15:54:00 +00:00
< / script >
< / body >
< / html >