Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
271 lines
8.6 KiB
JavaScript
271 lines
8.6 KiB
JavaScript
/* global MozActivity, Notification, Service */
|
|
|
|
'use strict';
|
|
|
|
(function(exports) {
|
|
|
|
/**
|
|
* This system module takes a screenshot of the currently running app
|
|
* or homescreen and stores it with DeviceStorage when the user
|
|
* presses the home and sleep buttons at the same time. It communicates
|
|
* with gecko code running in b2g/chrome/content/shell.js using a private
|
|
* event-based API. It is the gecko code that creates the screenshot.
|
|
*
|
|
* This script must be used with the defer attribute.
|
|
*
|
|
* @class Screenshot
|
|
*
|
|
*/
|
|
function Screenshot() {
|
|
this._started = false;
|
|
}
|
|
|
|
Screenshot.prototype = {
|
|
/** @lends Screenshot */
|
|
|
|
/**
|
|
* Assumption for making sure we have enough space to save the image.
|
|
*
|
|
* Assume that the maximum screenshot size is 4 bytes per device pixel
|
|
* plus a bit extra. In practice, with compression, our PNG files will be
|
|
* much smaller than this.
|
|
*
|
|
* @type {Number}
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
MAX_SCREENSHOT_SIZE:
|
|
window.innerWidth * window.devicePixelRatio *
|
|
window.innerHeight * window.devicePixelRatio * 4 + 4096,
|
|
|
|
/**
|
|
* Start to handle screenshot events.
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
start: function() {
|
|
if (this._started) {
|
|
throw 'Instance should not be start()\'ed twice.';
|
|
}
|
|
this._started = true;
|
|
|
|
Service.request('handleSystemMessageNotification', 'screenshot', this);
|
|
window.addEventListener('volumedown+sleep', this);
|
|
window.addEventListener('mozChromeEvent', this);
|
|
},
|
|
|
|
/**
|
|
* Stop handling screenshot events.
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
stop: function() {
|
|
if (!this._started) {
|
|
throw 'Instance was never start()\'ed but stop() is called.';
|
|
}
|
|
this._started = false;
|
|
|
|
Service.request('unhandleSystemMessageNotification', 'screenshot', this);
|
|
window.removeEventListener('volumedown+sleep', this);
|
|
window.removeEventListener('mozChromeEvent', this);
|
|
},
|
|
|
|
/**
|
|
* Handle screenshot events.
|
|
* @param {DOMEvent} evt DOM Event to handle.
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
handleEvent: function(evt) {
|
|
switch (evt.type) {
|
|
case 'volumedown+sleep':
|
|
this.takeScreenshot();
|
|
break;
|
|
|
|
case 'mozChromeEvent':
|
|
if (evt.detail.type === 'take-screenshot-success') {
|
|
this.handleTakeScreenshotSuccess(evt.detail.file);
|
|
} else if (evt.detail.type === 'take-screenshot-error') {
|
|
this._notify('screenshotFailed', evt.detail.error);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
console.debug('Unhandled event: ' + evt.type);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Gets called when a system message notification for a screenshots is being
|
|
* hit.
|
|
**/
|
|
handleSystemMessageNotification: function(message) {
|
|
this.openImage(message.body);
|
|
this.closeSystemMessageNotification(message);
|
|
},
|
|
|
|
closeSystemMessageNotification: function(msg) {
|
|
Notification.get({ tag: msg.tag }).then(notifs => {
|
|
notifs.forEach(notif => {
|
|
if (notif.tag) {
|
|
// Close notification with the matching tag
|
|
if (notif.tag === msg.tag) {
|
|
notif.close && notif.close();
|
|
}
|
|
} else {
|
|
// If we have notification without a tag, check on the body
|
|
if (notif.body === msg.body) {
|
|
notif.close && notif.close();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Actually take a screenshot (by do some check and send a mozContentEvent.)
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
takeScreenshot: function() {
|
|
// Give feedback that the screenshot request was received
|
|
navigator.vibrate(100);
|
|
|
|
// We don't need device storage here, but check to see that
|
|
// it is available before sending the screenshot request to chrome.
|
|
// If device storage is available, the callback will be called.
|
|
// Otherwise, an error message notification will be displayed.
|
|
this._getDeviceStorage(function() {
|
|
// Let chrome know we'd like a screenshot.
|
|
// This is a completely non-standard undocumented API
|
|
// for communicating with our chrome code.
|
|
var screenshotProps = {
|
|
detail: {
|
|
type: 'take-screenshot'
|
|
}
|
|
};
|
|
window.dispatchEvent(
|
|
new CustomEvent('mozContentEvent', screenshotProps));
|
|
});
|
|
},
|
|
|
|
openImage: function(filename) {
|
|
this._getDeviceStorage(function(storage) {
|
|
var request = storage.get(filename);
|
|
request.onsuccess = function() {
|
|
var imgblob = this.result;
|
|
/*jshint nonew: false */
|
|
new MozActivity({
|
|
name: 'open',
|
|
data: {
|
|
type: imgblob.type,
|
|
filename: filename,
|
|
blob: imgblob,
|
|
exitWhenHidden: true
|
|
}
|
|
});
|
|
};
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Handle the take-screenshot-success mozChromeEvent.
|
|
* @param {Blob} file Blob object received from the event.
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
handleTakeScreenshotSuccess: function(file) {
|
|
try {
|
|
this._getDeviceStorage(function(storage) {
|
|
var d = new Date();
|
|
d = new Date(d.getTime() - d.getTimezoneOffset() * 60000);
|
|
var filename = 'screenshots/' +
|
|
d.toISOString().slice(0, -5).replace(/[:T]/g, '-') + '.png';
|
|
|
|
var saveRequest = storage.addNamed(file, filename);
|
|
saveRequest.onsuccess = (function ss_onsuccess() {
|
|
// Vibrate again when the screenshot is saved
|
|
navigator.vibrate(100);
|
|
|
|
// Display filename in a notification
|
|
this._notify('screenshotSaved', filename, null,
|
|
this.openImage.bind(this, filename));
|
|
}).bind(this);
|
|
|
|
saveRequest.onerror = (function ss_onerror() {
|
|
this._notify('screenshotFailed', saveRequest.error.name);
|
|
}).bind(this);
|
|
});
|
|
} catch (e) {
|
|
console.log('exception in screenshot handler', e);
|
|
this._notify('screenshotFailed', e.toString());
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Get a DeviceStorage object and pass it to the callback.
|
|
* Or, if device storage is not available, display a notification.
|
|
* @param {Function} callback Callback to run.
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
_getDeviceStorage: function(callback) {
|
|
var storage = navigator.getDeviceStorage('pictures');
|
|
var availreq = storage.available();
|
|
|
|
availreq.onsuccess = (function() {
|
|
var state = availreq.result;
|
|
if (state === 'unavailable') {
|
|
this._notify('screenshotFailed', null, 'screenshotNoSDCard');
|
|
}
|
|
else if (state === 'shared') {
|
|
this._notify('screenshotFailed', null, 'screenshotSDCardInUse');
|
|
}
|
|
else if (state === 'available') {
|
|
var freereq = storage.freeSpace();
|
|
freereq.onsuccess = (function() {
|
|
if (freereq.result < this.MAX_SCREENSHOT_SIZE) {
|
|
this._notify('screenshotFailed', null, 'screenshotSDCardLow');
|
|
} else {
|
|
callback.call(this, storage);
|
|
}
|
|
}).bind(this);
|
|
freereq.onerror = (function() {
|
|
this._notify(
|
|
'screenshotFailed', freereq.error && freereq.error.name);
|
|
}).bind(this);
|
|
}
|
|
}).bind(this);
|
|
|
|
availreq.onerror = (function() {
|
|
this._notify(
|
|
'screenshotFailed', availreq.error && availreq.error.name);
|
|
}).bind(this);
|
|
},
|
|
|
|
/**
|
|
* Display a screenshot success or failure notification.
|
|
* Localize the first argument, and localize the third if the second is null
|
|
* @param {String} titleid l10n ID of the string to show.
|
|
* @param {String} body Label to show as body, or null.
|
|
* @param {String} bodyid l10n ID of the label to show as body.
|
|
* @param {String} onClick Optional handler if the notification is clicked
|
|
* @memberof Screenshot.prototype
|
|
*/
|
|
_notify: function notify(titleid, body, bodyid, onClick) {
|
|
var title = navigator.mozL10n.get(titleid) || titleid;
|
|
body = body || navigator.mozL10n.get(bodyid);
|
|
var notification = new window.Notification(title, {
|
|
body: body,
|
|
icon: '/style/icons/Gallery.png',
|
|
tag: 'screenshot:' + (new Date().getTime()),
|
|
data: {
|
|
systemMessageTarget: 'screenshot'
|
|
}
|
|
});
|
|
|
|
notification.onclick = function() {
|
|
notification.close();
|
|
if (onClick) {
|
|
onClick();
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
exports.Screenshot = Screenshot;
|
|
}(window));
|