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.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
354
gaia/apps/system/lockscreen/js/wallpaper_manager.js
Executable file
354
gaia/apps/system/lockscreen/js/wallpaper_manager.js
Executable file
|
|
@ -0,0 +1,354 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global ImageUtils, LazyLoader */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
const WALLPAPER_KEY = 'wallpaper.image';
|
||||
const WALLPAPER_VALID_KEY = 'wallpaper.image.valid';
|
||||
const DEFAULT_WALLPAPER_URL = '/resources/images/backgrounds/default.png';
|
||||
|
||||
/**
|
||||
* This system module reads the system wallpaper setting on startup
|
||||
* and monitors changes to that setting, broadcasting a
|
||||
* 'wallpaperchange' event with a blob: URL to tell the system and
|
||||
* lockscreen about the new wallpaper.
|
||||
*
|
||||
* If the wallpaper value read from the settings DB is a URL this
|
||||
* module converts it to a blob. If the wallpaper image does not
|
||||
* exactly match the size of the screen, this module resizes it
|
||||
* (lazy-loading shared/js/image_utils.js when needed). If the
|
||||
* wallpaper value is converted to a blob or resized, the modified
|
||||
* value is saved back to the settings DB so that it will not need
|
||||
* to be modified the next time it is read.
|
||||
*
|
||||
* start(), stop(), and getBlobURL() are the only public methods,
|
||||
* and stop() is only exposed for the benefit of unit
|
||||
* tests. _setWallpaper() is called on startup and whenever the
|
||||
* wallpaper.image setting changes. Each call to _setWallpaper()
|
||||
* eventually causes a call to _publish() which broadcasts the new
|
||||
* wallpaper event to the lockscreen and the rest of the system
|
||||
* app. The call to _publish() does not always happen directly,
|
||||
* however: _setWallpaper() may call _checkSize(), which calls
|
||||
* _publish(), or it may call _toBlob() which calls _checkSize().
|
||||
* Unless the build is mis-configured and the wallpaper in the
|
||||
* settings db and the fallback default wallpaper is broken, every
|
||||
* call to _setWallpaper() ends up broadcasting a 'wallpaperchange'
|
||||
* event with a valid blob: url for a wallpaper image that has the
|
||||
* same size as the screen.
|
||||
*
|
||||
* @class WallpaperManager
|
||||
*/
|
||||
function WallpaperManager() {
|
||||
this._started = false;
|
||||
this._blobURL = null;
|
||||
}
|
||||
|
||||
WallpaperManager.prototype = {
|
||||
/**
|
||||
* Bootstrap the module. Read the current wallpaper from the
|
||||
* settings db and pass it to _setWallpaper(). Also listen for
|
||||
* changes to the wallpaper and invoke _setWallpaper() for each
|
||||
* one.
|
||||
*/
|
||||
start: function() {
|
||||
if (this._started) {
|
||||
throw 'Instance should not be start()\'ed twice.';
|
||||
}
|
||||
this._started = true;
|
||||
debug('started');
|
||||
|
||||
// Query the wallpaper
|
||||
var lock = navigator.mozSettings.createLock();
|
||||
var query = lock.get(WALLPAPER_KEY);
|
||||
query.onsuccess = function() {
|
||||
var wallpaper = query.result[WALLPAPER_KEY];
|
||||
if (!wallpaper) {
|
||||
debug('no wallpaper found at startup; using default');
|
||||
this._setWallpaper(DEFAULT_WALLPAPER_URL);
|
||||
}
|
||||
else if (wallpaper instanceof Blob) {
|
||||
// If the wallpaper is a blob, first go see if we have already
|
||||
// validated it size. Because if we have, we don't have to check
|
||||
// the size again or even load the code to check its size.
|
||||
var query2 = lock.get(WALLPAPER_VALID_KEY);
|
||||
query2.onsuccess = function() {
|
||||
var valid = query2.result[WALLPAPER_VALID_KEY];
|
||||
this._setWallpaper(wallpaper, valid);
|
||||
}.bind(this);
|
||||
}
|
||||
else {
|
||||
// If the wallpaper is not a blob, just pass it to _setWallpaper
|
||||
// and try to convert it to a blob there.
|
||||
this._setWallpaper(wallpaper);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
// And register a listener so we'll be notified of future changes
|
||||
// to the wallpaper
|
||||
this.observer = function(e) {
|
||||
this._setWallpaper(e.settingValue);
|
||||
}.bind(this);
|
||||
navigator.mozSettings.addObserver(WALLPAPER_KEY, this.observer);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop the module an stop listening for changes to the wallpaper setting.
|
||||
* This method is only used by unit tests.
|
||||
*/
|
||||
stop: function() {
|
||||
if (!this._started) { return; }
|
||||
navigator.mozSettings.removeObserver(WALLPAPER_KEY, this.observer);
|
||||
this._started = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the blob URL saved from earlier wallpaper change event
|
||||
* The lockscreen may miss the event and needs to look the URL up here.
|
||||
* @returns {String} the blob URL
|
||||
*/
|
||||
getBlobURL: function() {
|
||||
if (!this._started) { return; }
|
||||
return this._blobURL;
|
||||
},
|
||||
|
||||
//
|
||||
// This method is called on startup and when the wallpaper
|
||||
// changes. It always causes _publish() to be invoked and a
|
||||
// "wallpaperchange" event to be broadcast to interested
|
||||
// listeners. If the new value is a blob that is already
|
||||
// validated, then _publish() is called directly. Otherwise, it is
|
||||
// called indirectly by _toBlob() or _checkSize().
|
||||
//
|
||||
_setWallpaper: function(value, valid) {
|
||||
if (!this._started) { return; }
|
||||
|
||||
// If we are called because we just saved a resized blob back
|
||||
// to the settings db, then ignore the call.
|
||||
if (value instanceof Blob && value.size === this.savedBlobSize) {
|
||||
this.savedBlobSize = false;
|
||||
return;
|
||||
}
|
||||
|
||||
debug('new wallpaper', valid ? 'size already validated' : '');
|
||||
|
||||
if (typeof value === 'string') {
|
||||
this._toBlob(value);
|
||||
}
|
||||
else if (value instanceof Blob) {
|
||||
// If this blob has already been validated, we can just display it.
|
||||
// Otherwise we need to check its size first
|
||||
if (valid) {
|
||||
this._publish(value);
|
||||
}
|
||||
else {
|
||||
this._checkSize(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The value in the settings database is invalid, so
|
||||
// use the default image. Note that this will update the
|
||||
// settings db with a valid value.
|
||||
debug('Invalid wallpaper value in settings;',
|
||||
'reverting to default wallpaper.');
|
||||
this._toBlob(DEFAULT_WALLPAPER_URL);
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// This method expects a wallpaper URL (possibly a data: URL) and
|
||||
// uses XHR to convert it to a blob. If it succeeds, it passes the
|
||||
// blob to _checkSize() which resizes it if needed and calls
|
||||
// _publish() to broadcast the new wallpaper.
|
||||
//
|
||||
_toBlob: function(url) {
|
||||
if (!this._started) { return; }
|
||||
debug('converting wallpaper url to blob');
|
||||
|
||||
// If we trying to convert the default wallpaper url to a blob
|
||||
// note that because there is some error recovery code that behaves
|
||||
// differently in that last resort case.
|
||||
this.tryingDefaultWallpaper = (url === DEFAULT_WALLPAPER_URL);
|
||||
|
||||
// If the settings db had a string in it we assume it is a
|
||||
// relative url or data: url and try to read it with XHR.
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.send();
|
||||
xhr.onload = function() {
|
||||
// Once we've loaded the wallpaper as a blob, verify its size.
|
||||
// We pass true as the second argument to force it to be saved
|
||||
// back to the db (as a blob) even if the size is okay.
|
||||
this._checkSize(xhr.response, true);
|
||||
}.bind(this);
|
||||
xhr.onerror = function() {
|
||||
// If we couldn't load the url and if it was something other
|
||||
// than the default wallpaper url, then try again with the default.
|
||||
if (!this.tryingDefaultWallpaper) {
|
||||
debug('corrupt wallpaper url in settings;',
|
||||
'reverting to default wallpaper');
|
||||
this._toBlob(DEFAULT_WALLPAPER_URL);
|
||||
}
|
||||
else {
|
||||
// This was our last resort, and it failed, so no wallpaper
|
||||
// image is available.
|
||||
console.error('Cannot load wallpaper from', url);
|
||||
}
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
//
|
||||
// This method checks the dimensions of the image blob and crops
|
||||
// and resizes the image if necessary so that it is exactly the
|
||||
// same size as the screen. If the image was resized, or if it was
|
||||
// read from a URL, then this method saves the new blob back to
|
||||
// the settings db and marks it as valid. If the image was not
|
||||
// resized, then the image is marked as valid so that the check
|
||||
// does not need to be performed when the phone is rebooted. In
|
||||
// either case, after the image is saved and/or validated, this
|
||||
// method calls _publish() to broadcast the new wallpaper.
|
||||
//
|
||||
// If the blob does not hold a valid image, that will be
|
||||
// discovered while attempting to check its size and in that case,
|
||||
// this method falls back on the default wallpaper by calling
|
||||
// _toBlob() with the default wallpaper URL.
|
||||
//
|
||||
// This method lazy-loads ImageUtils from shared/js/image_utils.js.
|
||||
// Once a wallpaper has had its size checked once, it is marked as
|
||||
// valid in the settings db, so these image utilities will not
|
||||
// need to be loaded into the system app on subsequent reboots.
|
||||
//
|
||||
_checkSize: function(blob, needsToBeSaved) {
|
||||
if (!this._started) { return; }
|
||||
debug('resizing wallpaper if needed');
|
||||
|
||||
// How big (in device pixels) is the screen?
|
||||
var screenWidth = Math.ceil(screen.width * window.devicePixelRatio);
|
||||
var screenHeight = Math.ceil(screen.height * window.devicePixelRatio);
|
||||
|
||||
// For performance we need to guarantee that the size of the wallpaper
|
||||
// is exactly the same as the size of the screen. LazyLoad the
|
||||
// ImageUtils module, and call its resizeAndCropToCover() method to
|
||||
// resize and crop the image as needed so that it is the right size.
|
||||
// Note that this utility funtion can determine the size of an image
|
||||
// without decoding it and if the image is already the right size
|
||||
// it will not modify it.
|
||||
LazyLoader.load('shared/js/image_utils.js', function() {
|
||||
ImageUtils
|
||||
.resizeAndCropToCover(blob, screenWidth, screenHeight, ImageUtils.PNG)
|
||||
.then(
|
||||
function resolve(resizedBlob) {
|
||||
// If the blob changed or if the second argument was true
|
||||
// then we need to save the blob back to the settings db
|
||||
if (resizedBlob !== blob || needsToBeSaved) {
|
||||
this._save(resizedBlob);
|
||||
}
|
||||
else {
|
||||
// If the blob didn't change we don't have to save it,
|
||||
// but we do need to mark it as valid
|
||||
this._validate();
|
||||
}
|
||||
|
||||
// Display the wallpaper
|
||||
this._publish(resizedBlob);
|
||||
}.bind(this),
|
||||
function reject(error) {
|
||||
// This will only happen if the settings db contains a blob that
|
||||
// is not actually an image. If that happens for some reason,
|
||||
// fall back on the default wallpaper.
|
||||
if (!this.tryingDefaultWallpaper) {
|
||||
debug('Corrupt wallpaper image in settings;',
|
||||
'reverting to default wallpaper.');
|
||||
this._toBlob(DEFAULT_WALLPAPER_URL);
|
||||
}
|
||||
else {
|
||||
// We were already trying the default wallpaper and it failed.
|
||||
// So we just give up in this case.
|
||||
console.error('Default wallpaper image is invalid');
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
//
|
||||
// This method sets a property in the settings db to indicate that
|
||||
// the current wallpaper is the same size as the screen. Setting
|
||||
// this property is an optimization that allows us to skip the
|
||||
// call to _checkSize() on subsequent startups. This method
|
||||
// returns synchronously and does not wait for the settings db
|
||||
// operation to complete.
|
||||
//
|
||||
_validate: function() {
|
||||
if (!this._started) { return; }
|
||||
debug('marking wallpaper as valid');
|
||||
var settings = {};
|
||||
settings[WALLPAPER_VALID_KEY] = true; // We've checked its size
|
||||
navigator.mozSettings.createLock().set(settings);
|
||||
},
|
||||
|
||||
//
|
||||
// This method saves the wallpaper blob to the settings db and
|
||||
// also marks it as valid so that we know on subsequent startups
|
||||
// that its size has already been checked. This method returns
|
||||
// synchronously and does not wait for the settings db operation
|
||||
// to complete.
|
||||
//
|
||||
_save: function(blob) {
|
||||
if (!this._started) { return; }
|
||||
debug('saving converted or resized wallpaper to settings');
|
||||
|
||||
// Set a flag so that we don't repeat this whole process when
|
||||
// we're notified about this save. The flag contains the size of
|
||||
// the blob we're saving so it is very unlikely that we'll have
|
||||
// a race condition.
|
||||
this.savedBlobSize = blob.size;
|
||||
|
||||
// Now save the blob to the settings db, and also save a flag
|
||||
// that indicates that we've already checked the size of the image.
|
||||
// This allows us to skip the check at boot time.
|
||||
var settings = {};
|
||||
settings[WALLPAPER_KEY] = blob;
|
||||
settings[WALLPAPER_VALID_KEY] = true; // We've checked its size
|
||||
navigator.mozSettings.createLock().set(settings);
|
||||
},
|
||||
|
||||
//
|
||||
// This method creates a blob: URL for the specfied blob and publishes
|
||||
// the URL via a 'wallpaperchange' event. If there was a previous
|
||||
// wallpaper, its blob: URL is revoked. This method is synchronous.
|
||||
//
|
||||
_publish: function(blob) {
|
||||
if (!this._started) { return; }
|
||||
debug('publishing wallpaperchange event');
|
||||
|
||||
// If we have a blob:// url for previous wallpaper, release it now
|
||||
if (this._blobURL) {
|
||||
URL.revokeObjectURL(this._blobURL);
|
||||
}
|
||||
|
||||
// Create a new blob:// url for this blob
|
||||
this._blobURL = URL.createObjectURL(blob);
|
||||
|
||||
// And tell the system about it.
|
||||
window.dispatchEvent(new CustomEvent('wallpaperchange',
|
||||
{ detail: { url: this._blobURL } }));
|
||||
}
|
||||
};
|
||||
|
||||
// Log debug messages
|
||||
function debug(...args) {
|
||||
if (WallpaperManager.DEBUG) {
|
||||
args.unshift('[WallpaperManager]');
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
}
|
||||
WallpaperManager.DEBUG = false; // Set to false to silence debug output
|
||||
|
||||
/** @exports WallpaperManager */
|
||||
exports.WallpaperManager = WallpaperManager;
|
||||
}(window));
|
||||
Loading…
Reference in a new issue