-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
backup.js
135 lines (131 loc) · 5 KB
/
backup.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* 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();
});
});