VulpesOS/gaia/distros/spark/apps/studio/js/generation.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

258 lines
7.5 KiB
JavaScript
Executable file

/*global
MimeMapper
*/
(function(exports) {
'use strict';
function proposeDownload(blob) {
console.log('proposeDownload');
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'app.zip';
a.hidden = true;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(blob);
}
function uninstallIfNeeded(theme) {
if (!theme.manifestURL) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
var all = navigator.mozApps.mgmt.getAll();
all.onsuccess = (e) => {
var toDelete = e.target.result.find((app) => {
return (app.manifestURL === theme.manifestURL);
});
if (toDelete) {
var req = navigator.mozApps.mgmt.uninstall(toDelete);
req.onsuccess = resolve;
req.onerror = reject;
} else {
reject(new Error('could not find the app to uninstall'));
}
};
all.onerror = reject;
});
}
function importBlob(blob, enable) {
console.log('importBlob');
if (!navigator.getDeviceStorage) {
return Promise.reject(new Error('getDeviceStorage is unavailable.'));
}
// Save the blob to a file because we don't support importing memory blobs.
var sdcard = navigator.getDeviceStorage('sdcard');
return new Promise((resolve, reject) => {
function sendError(msg) {
console.log(msg);
reject(msg);
}
if (!sdcard) {
sendError('No SDCard!');
return;
}
var fileName = 'temp-app.zip';
var delReq = sdcard.delete(fileName);
delReq.onsuccess = delReq.onerror = function() {
var req = sdcard.addNamed(blob, fileName);
req.onsuccess = function(e) {
var getReq = sdcard.get(fileName);
getReq.onsuccess = function() {
var file = this.result;
navigator.mozApps.mgmt.import(file).then(
function(app) {
if (enable) {
var setting = { 'theme.selected' : app.manifestURL };
var req = navigator.mozSettings.createLock().set(setting);
req.onsuccess = resolve.bind(null, app.manifestURL);
req.onerror = sendError;
} else {
resolve(app.manifestURL);
}
},
function(error) { sendError('Error importing: ' + error.name); }
);
}
getReq.onerror = function() {
sendError('Error getting file: ' + this.error.name);
}
}
req.onerror = function(e) {
sendError('Error saving blob: ' + this.error.name);
}
};
});
}
function exportTheme(enable, theme) {
// Worker path configuration.
zip.workerScriptsPath = './js/libs/';
console.log('exportTheme ' + theme.title);
// TODO: use a stable appId.
var appId = 'app' + Math.round(Math.random() * 100000000);
var app = new AppZip();
app.metaData = {
installOrigin: 'http://gaiamobile.org',
manifestURL: 'app://theme' + appId + '.gaiamobile.org/update.webapp',
version: 1
};
app.manifest = {
name: theme.title,
package_path: '/application.zip',
};
// Build a simple package with a manifest and index.html
var inner = new PackageHelper();
var manifest = {
name: theme.title,
role: "theme",
type: "certified",
origin: 'app://theme' + appId + '.gaiamobile.org'
};
return inner.init().then(() => {
function addManifest() {
return inner.addResource('manifest.webapp', JSON.stringify(manifest));
}
function addCSS() {
return inner.addResource('shared/elements/gaia-theme/gaia-theme.css', userStyle(theme));
}
function addWallpaper(blob) {
// if we don't have a (valid) wallpaper we still want an empty JSON
var wallpaperFile = null;
var wallpaper = { homescreen: null };
if (blob) {
var type = MimeMapper.guessExtensionFromType(blob.type);
if (type) {
wallpaperFile = 'wallpaper.' + type;
wallpaper.homescreen = '/' + wallpaperFile;
} else {
console.error('Unrecognized type: ' + blob.type);
}
}
// We can't use Promise.all to add both files because it corrupts the
// zip
return Promise.resolve()
.then(() => inner.addResource(
'wallpaper.json', JSON.stringify(wallpaper)))
.then(function() {
if (wallpaperFile) {
inner.addResource(wallpaperFile, blob);
}
});
}
var zipPromise = addManifest().then(addCSS);
var image = null;
if (theme.autotheme) {
image = theme.autotheme.image;
}
zipPromise = zipPromise.then(() => addWallpaper(image));
return zipPromise.then(inner.asBlob.bind(inner))
.then((packageBlob) => {
app.packageblob = packageBlob;
return app.asBlob();
}).then((appBlob) => {
return uninstallIfNeeded(theme)
.then(() => {
return importBlob(appBlob, enable);
})
.then((manifestURL) => {
theme.manifestURL = manifestURL;
return Storage.updateTheme(theme);
})
.then(() => Promise.resolve(image &&
navigator.mozSettings.createLock().set({
'wallpaper.image': image
})
))
.catch((e) => {
console.error('Error while importing blob', e);
// Desktop debug
// proposeDownload(appBlob);
alert('This device is not theming compatible');
});
});
});
}
function userStyle(theme) {
var roots = [':root', '.theme-productivity', '.theme-communications',
'.theme-settings', '.theme-media'];
var str = '';
roots.forEach(function(root) {
str += root + ' {\n';
var group;
if (root === ':root') {
group = 'default';
// Add :root CSS variables here.
str += '--transition-duration: 0.2s;\n';
} else {
group = root.split('-')[1];
}
Object.keys(theme.groups[group]).forEach(function(sectionKey) {
var section = theme.groups[group][sectionKey];
Object.keys(section).forEach(function(key) {
str += key + ': ' + section[key] + ';\n';
});
});
str += '}\n';
});
return str;
}
function fakeInstall(css) {
var override = document.createElement('style');
override.innerHTML = css;
document.body.appendChild(override);
}
function checkIfSelected(theme) {
return new Promise((resolve, reject) => {
var req = navigator.mozSettings.createLock().get('theme.selected');
req.onsuccess = function(e) {
resolve([(req.result['theme.selected'] == theme.manifestURL), theme]);
};
req.onerror = reject;
});
}
exports.Generation = {
installTheme: function(id, enable) {
return Storage.fetchTheme(id)
.then(checkIfSelected)
.then((results) => {
return exportTheme(results[0] || enable, results[1]);
});
},
uninstallIfNeeded: function(theme) {
return checkIfSelected(theme)
.then((results) => {
var selected = results[0];
if (selected) {
alert("You can't remove the selected theme");
} else {
return uninstallIfNeeded(theme);
}
});
}
};
})(window);