2022-03-15 12:47:57 +00:00
/* 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..." ) ;
2022-03-15 15:24:59 +00:00
Comms . reset ( )
. then ( ( ) => Comms . showMessage ( "Backing up..." ) )
. then ( ( ) => Comms . listFiles ( { sf : false } ) )
. then ( f => {
2022-03-15 12:47:57 +00:00
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 ) => {
2022-03-17 10:14:25 +00:00
if ( filename == ".firmware" ) {
console . log ( "Ignoring .firmware file" ) ;
return ;
}
2022-03-15 12:47:57 +00:00
promise = promise . then ( ( ) => {
Progress . hide ( { sticky : true } ) ;
var percent = n / fileCount ;
2022-03-17 10:43:18 +00:00
Progress . show ( { title : ` Download ${ filename } ` , sticky : true , min : percent , max : percent + ( 1 / fileCount ) , percent : 0 } ) ;
2022-03-15 12:47:57 +00:00
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 ;
2022-03-17 10:43:18 +00:00
Progress . show ( { title : ` Download ${ filename } ` , sticky : true , min : percent , max : percent + ( 1 / fileCount ) , percent : 0 } ) ;
2022-03-15 12:47:57 +00:00
return Comms . readStorageFile ( filename ) . then ( data => zipStorageFiles . file ( filename , data ) ) ;
} ) ;
} ) ;
}
return promise ;
2022-03-15 15:24:59 +00:00
} ) . then ( ( ) => {
return Comms . showMessage ( Const . MESSAGE _RELOAD ) ;
2022-03-15 12:47:57 +00:00
} ) . then ( ( ) => {
2022-03-15 16:43:22 +00:00
return zip . generateAsync ( { type : "binarystring" } ) ;
2022-03-15 12:47:57 +00:00
} ) . then ( content => {
Progress . hide ( { sticky : true } ) ;
showToast ( 'Backup complete!' , 'success' ) ;
2024-09-12 12:03:03 +00:00
if ( typeof Android !== "undefined" && typeof Android . saveFile === 'function' ) {
// Recent Gadgetbridge version that provides the saveFile interface
2024-09-13 08:07:19 +00:00
Android . saveFile ( "Banglejs backup.zip" , "application/zip" , btoa ( content ) ) ;
2024-09-12 12:03:03 +00:00
} else {
Espruino . Core . Utils . fileSaveDialog ( content , "Banglejs backup.zip" ) ;
}
2022-03-15 12:47:57 +00:00
} ) . 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 ) {
2023-05-04 11:06:31 +00:00
return showPrompt ( "Restore from ZIP" , "Are you sure? This will overwrite existing apps" ) ;
2022-03-15 12:47:57 +00:00
} ) . 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 ) ;
2022-03-30 13:36:52 +00:00
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 ) ) {
2022-03-15 12:47:57 +00:00
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 ;
} )
2023-05-04 11:06:31 +00:00
. 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 ( ) ;
} } )
2022-03-15 12:47:57 +00:00
. 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 ) )
2022-08-11 09:16:07 +00:00
. then ( ( ) => getInstalledApps ( true ) )
2022-03-15 12:47:57 +00:00
. 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 ( ) ;
} ) ;
} ) ;