mirror of https://github.com/espruino/BangleApps
136 lines
5.0 KiB
JavaScript
136 lines
5.0 KiB
JavaScript
/* Code to handle Backup/Restore functionality */
|
|
|
|
const BACKUP_STORAGEFILE_DIR = "storage-files";
|
|
|
|
function bangleDownload() {
|
|
var zip = new JSZip();
|
|
Progress.show({title:"Scanning...",sticky:true});
|
|
var normalFiles, storageFiles;
|
|
console.log("Listing normal files...");
|
|
Comms.reset()
|
|
.then(() => Comms.showMessage("Backing up..."))
|
|
.then(() => Comms.listFiles({sf:false}))
|
|
.then(f => {
|
|
normalFiles = f;
|
|
console.log(" - "+f.join(","));
|
|
console.log("Listing StorageFiles...");
|
|
return Comms.listFiles({sf:true});
|
|
}).then(f => {
|
|
storageFiles = f;
|
|
console.log(" - "+f.join(","));
|
|
var fileCount = normalFiles.length + storageFiles.length;
|
|
var promise = Promise.resolve();
|
|
// Normal files
|
|
normalFiles.forEach((filename,n) => {
|
|
if (filename==".firmware") {
|
|
console.log("Ignoring .firmware file");
|
|
return;
|
|
}
|
|
promise = promise.then(() => {
|
|
Progress.hide({sticky: true});
|
|
var percent = n/fileCount;
|
|
Progress.show({title:`Download ${filename}`,sticky:true,min:percent,max:percent+(1/fileCount),percent:0});
|
|
return Comms.readFile(filename).then(data => zip.file(filename,data));
|
|
});
|
|
});
|
|
// Storage files
|
|
if (storageFiles.length) {
|
|
var zipStorageFiles = zip.folder(BACKUP_STORAGEFILE_DIR);
|
|
storageFiles.forEach((filename,n) => {
|
|
promise = promise.then(() => {
|
|
Progress.hide({sticky: true});
|
|
var percent = (normalFiles.length+n)/fileCount;
|
|
Progress.show({title:`Download ${filename}`,sticky:true,min:percent,max:percent+(1/fileCount),percent:0});
|
|
return Comms.readStorageFile(filename).then(data => zipStorageFiles.file(filename,data));
|
|
});
|
|
});
|
|
}
|
|
return promise;
|
|
}).then(() => {
|
|
return Comms.showMessage(Const.MESSAGE_RELOAD);
|
|
}).then(() => {
|
|
return zip.generateAsync({type:"binarystring"});
|
|
}).then(content => {
|
|
Progress.hide({ sticky: true });
|
|
showToast('Backup complete!', 'success');
|
|
if (typeof Android !== "undefined" && typeof Android.saveFile === 'function') {
|
|
// Recent Gadgetbridge version that provides the saveFile interface
|
|
Android.saveFile("Banglejs backup.zip", "application/zip", btoa(content));
|
|
} else {
|
|
Espruino.Core.Utils.fileSaveDialog(content, "Banglejs backup.zip");
|
|
}
|
|
}).catch(err => {
|
|
Progress.hide({ sticky: true });
|
|
showToast('Backup failed, ' + err, 'error');
|
|
});
|
|
}
|
|
|
|
function bangleUpload() {
|
|
Espruino.Core.Utils.fileOpenDialog({
|
|
id:"backup",
|
|
type:"arraybuffer",
|
|
mimeType:".zip,application/zip"}, function(data) {
|
|
if (data===undefined) return;
|
|
var promise = Promise.resolve();
|
|
var zip = new JSZip();
|
|
var cmds = "";
|
|
zip.loadAsync(data).then(function(zip) {
|
|
return showPrompt("Restore from ZIP","Are you sure? This will overwrite existing apps");
|
|
}).then(()=>{
|
|
Progress.show({title:`Reading ZIP`});
|
|
zip.forEach(function (path, file){
|
|
console.log("path");
|
|
promise = promise
|
|
.then(() => file.async("string"))
|
|
.then(data => {
|
|
console.log("decoded", path);
|
|
if (data.length==0) { // https://github.com/espruino/BangleApps/issues/1593
|
|
console.log("Can't restore files of length 0, ignoring "+path);
|
|
} else if (path.startsWith(BACKUP_STORAGEFILE_DIR)) {
|
|
path = path.substr(BACKUP_STORAGEFILE_DIR.length+1);
|
|
cmds += AppInfo.getStorageFileUploadCommands(path, data)+"\n";
|
|
} else if (!path.includes("/")) {
|
|
cmds += AppInfo.getFileUploadCommands(path, data)+"\n";
|
|
} else console.log("Ignoring "+path);
|
|
});
|
|
});
|
|
return promise;
|
|
})
|
|
.then(()=>new Promise(resolve => {
|
|
showPrompt("Erase Storage","Erase Storage? If restoring a complete backup you should erase storage, but in some cases you may want to upload files from a ZIP while keeping your Bangle's existing data.").then(()=>resolve(true), ()=>resolve(false));
|
|
}))
|
|
.then(eraseStorage => {
|
|
if (eraseStorage) {
|
|
Progress.hide({sticky:true});
|
|
Progress.show({title:`Erasing...`});
|
|
return Comms.removeAllApps();
|
|
}})
|
|
.then(() => {
|
|
Progress.hide({sticky:true});
|
|
Progress.show({title:`Restoring...`, sticky:true});
|
|
return Comms.showMessage(`Restoring...`); })
|
|
.then(() => Comms.write("\x10"+Comms.getProgressCmd()+"\n"))
|
|
.then(() => Comms.uploadCommandList(cmds, 0, cmds.length))
|
|
.then(() => getInstalledApps(true))
|
|
.then(() => Comms.showMessage(Const.MESSAGE_RELOAD))
|
|
.then(() => {
|
|
Progress.hide({sticky:true});
|
|
showToast('Restore complete!', 'success');
|
|
})
|
|
.catch(err => {
|
|
Progress.hide({sticky:true});
|
|
showToast('Restore failed, ' + err, 'error');
|
|
});
|
|
return promise;
|
|
});
|
|
}
|
|
|
|
window.addEventListener('load', (event) => {
|
|
document.getElementById("downloadallapps").addEventListener("click",event=>{
|
|
bangleDownload();
|
|
});
|
|
document.getElementById("uploadallapps").addEventListener("click",event=>{
|
|
bangleUpload();
|
|
});
|
|
});
|