Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
260 lines
7.1 KiB
JavaScript
Executable file
260 lines
7.1 KiB
JavaScript
Executable file
/* global Promise */
|
|
'use strict';
|
|
|
|
/*
|
|
* builtInRingtones is an object that provides access to the system-provided
|
|
* ringtones on the device. It has the following methods:
|
|
*
|
|
* list():
|
|
* Lists all the built-in tones of a given type.
|
|
*
|
|
* @param {String} toneType The type of tone to list ('ringtone' or
|
|
* 'alerttone').
|
|
* @return {Promise} A promise returning an array of BuiltInRingtones
|
|
* representing each tone.
|
|
*
|
|
* get():
|
|
* Gets a tone of the given type and id.
|
|
*
|
|
* @param {String} id The ID of the tone to get.
|
|
* @return {Promise} A promise returning the requested BuiltInRingtone.
|
|
*
|
|
* toneTypes:
|
|
* An array of the valid tone types ('ringtone' and 'alerttone').
|
|
*/
|
|
window.builtInRingtones = (function() {
|
|
var ID_PREFIX = 'builtin:';
|
|
var BASE_URLS = {
|
|
'ringtone': '/shared/resources/media/ringtones/',
|
|
'alerttone': '/shared/resources/media/notifications/'
|
|
};
|
|
|
|
var mimeTypeMap = {
|
|
'.mp3': 'audio/mp3',
|
|
'.mp4': 'audio/mp4',
|
|
'.ogg': 'audio/ogg',
|
|
'.opus': 'audio/ogg'
|
|
};
|
|
|
|
/**
|
|
* Try to guess the MIME type of a file based on its extension. It's a shame
|
|
* XHRs don't do this for us automatically!
|
|
*
|
|
* @param {String} filename The filename of the ringtone.
|
|
* @return {String} The MIME type.
|
|
*/
|
|
function inferMimeType(filename) {
|
|
var dot = filename.lastIndexOf('.');
|
|
if (dot === -1) {
|
|
console.warn('Couldn\'t infer mimetype for ' + filename);
|
|
return 'application/octet-stream';
|
|
}
|
|
var ext = filename.substr(dot);
|
|
return mimeTypeMap[ext] || 'application/octet-stream';
|
|
}
|
|
|
|
/**
|
|
* Create a new built-in ringtone object.
|
|
*
|
|
* @param {String} toneType The kind of tone (ringtone or alerttone).
|
|
* @param {String} filename The filename of the ringtone.
|
|
* @param {Object} toneDef The tone's definition from list.json.
|
|
*/
|
|
function BuiltInRingtone(toneType, filename, toneDef) {
|
|
this._toneType = toneType;
|
|
this._filename = filename;
|
|
this._l10nID = toneDef.l10nID;
|
|
}
|
|
|
|
BuiltInRingtone.prototype = {
|
|
/**
|
|
* @return {String} The filename without the extension.
|
|
*/
|
|
get _rootName() {
|
|
// Strip the file extension for the ID to make it easier to change the
|
|
// file extensions in the future.
|
|
return this._filename.replace(/\.\w+$/, '');
|
|
},
|
|
|
|
/**
|
|
* @return {String} The localized name of the tone. Assumes that mozL10n has
|
|
* been initialized.
|
|
*/
|
|
get name() {
|
|
return navigator.mozL10n.get(this.l10nID);
|
|
},
|
|
|
|
/**
|
|
* @return {String} The filename of the ringtone.
|
|
*/
|
|
get filename() {
|
|
return this._filename;
|
|
},
|
|
|
|
/**
|
|
* @return {String} The l10n ID for the tone's name.
|
|
*/
|
|
get l10nID() {
|
|
return this._l10nID;
|
|
},
|
|
|
|
/**
|
|
* @return {String} A unique ID for the tone.
|
|
*/
|
|
get id() {
|
|
return ID_PREFIX + this._toneType + '/' + this._rootName;
|
|
},
|
|
|
|
/**
|
|
* @return {String} The type of the tone. Either "ringtone" or "alerttone".
|
|
*/
|
|
get type() {
|
|
return this._toneType;
|
|
},
|
|
|
|
/**
|
|
* @return {String} A URL pointing to the tone's audio data.
|
|
*/
|
|
get url() {
|
|
return BASE_URLS[this._toneType] + this._filename;
|
|
},
|
|
|
|
/**
|
|
* @return {Boolean} Whether this ringtone is shareable (always true).
|
|
*/
|
|
get shareable() {
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* @return {Boolean} Whether this ringtone is deletable (always false).
|
|
*/
|
|
get deletable() {
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Gets a blob for this tone.
|
|
*
|
|
* @return {Promise} A promise returning a Blob of the audio data.
|
|
*/
|
|
getBlob: function() {
|
|
var url = this.url;
|
|
return new Promise(function(resolve, reject) {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('GET', url);
|
|
xhr.overrideMimeType(inferMimeType(url));
|
|
xhr.responseType = 'blob';
|
|
xhr.send();
|
|
xhr.onload = function() {
|
|
resolve(xhr.response);
|
|
};
|
|
xhr.onerror = function() {
|
|
var err = new Error('Could not read sound file: ' + url +
|
|
' (status: ' + xhr.status + ')');
|
|
console.error(err);
|
|
reject(err);
|
|
};
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get the type of a BuiltInRingtone from its ID.
|
|
*
|
|
* @param {String} id The BuiltInRingtone's ID.
|
|
* @return {String} The tone's type ('ringtone' or 'alerttone').
|
|
*/
|
|
function idToToneType(id) {
|
|
var slash = id.indexOf('/');
|
|
if (id.indexOf(ID_PREFIX) !== 0 || slash === -1) {
|
|
throw new Error('invalid id: ' + id);
|
|
}
|
|
return id.substring(ID_PREFIX.length, slash);
|
|
}
|
|
|
|
/**
|
|
* Check if a BuiltInRingtone ID refers to a particular filename.
|
|
*
|
|
* @param {String} id The BuiltInRingtone's ID.
|
|
* @param {String} toneType The type of the tone held in `filename`.
|
|
* @param {String} filename The filename.
|
|
* @return {Boolean} True if the id and filename match, false otherwise.
|
|
*/
|
|
function idMatchesFilename(id, toneType, filename) {
|
|
return id === ID_PREFIX + toneType + '/' + filename.replace(/\.\w+$/, '');
|
|
}
|
|
|
|
// Our cache of tone definitions from the list.json files.
|
|
var toneDefsCache = {};
|
|
|
|
/**
|
|
* Read the list.json file to get the names of all sounds we know about.
|
|
* This function caches its results so that subsequent calls are speedy.
|
|
*
|
|
* @param {String} toneType The type of tones to get ('ringtone' or
|
|
* 'alerttone').
|
|
* @return {Promise} A promise returning an array of filenames.
|
|
*/
|
|
function getSoundFilenames(toneType) {
|
|
if (!(toneType in BASE_URLS)) {
|
|
throw new Error('tone type not supported: ' + toneType);
|
|
}
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
if (toneType in toneDefsCache) {
|
|
resolve(toneDefsCache[toneType]);
|
|
return;
|
|
}
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('GET', BASE_URLS[toneType] + 'list.json');
|
|
xhr.responseType = 'json';
|
|
xhr.send(null);
|
|
|
|
xhr.onload = function() {
|
|
toneDefsCache[toneType] = xhr.response;
|
|
resolve(xhr.response);
|
|
};
|
|
|
|
xhr.onerror = function() {
|
|
var err = new Error('Could not read sounds list for ' + toneType +
|
|
' (status: ' + xhr.status + ')');
|
|
console.error(err);
|
|
reject(err);
|
|
};
|
|
});
|
|
}
|
|
|
|
function list(toneType) {
|
|
return getSoundFilenames(toneType).then(function(toneDefs) {
|
|
var tones = [];
|
|
for (var filename in toneDefs) {
|
|
tones.push(new BuiltInRingtone(toneType, filename, toneDefs[filename]));
|
|
}
|
|
return tones;
|
|
});
|
|
}
|
|
|
|
function get(id) {
|
|
return new Promise(function(resolve, reject) {
|
|
var toneType = idToToneType(id);
|
|
resolve(getSoundFilenames(toneType).then(function(toneDefs) {
|
|
for (var filename in toneDefs) {
|
|
if (idMatchesFilename(id, toneType, filename)) {
|
|
return new BuiltInRingtone(toneType, filename, toneDefs[filename]);
|
|
}
|
|
}
|
|
var err = new Error('No ' + toneType + ' found with id = ' + id);
|
|
console.error(err);
|
|
throw err;
|
|
}));
|
|
});
|
|
}
|
|
|
|
return {
|
|
list: list,
|
|
get: get,
|
|
get toneTypes() { return Object.keys(BASE_URLS); }
|
|
};
|
|
})();
|