Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
258 lines
7.5 KiB
JavaScript
Executable file
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);
|