VulpesOS/gaia/apps/system/js/audio_channel_controller.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

255 lines
6.6 KiB
JavaScript
Executable file

/* global BaseUI */
'use strict';
(function(exports) {
var FADE_IN_VOLUME = 1;
var FADE_OUT_VOLUME = 0.2;
var VIBRATION_DURATION = 1000;
/**
* AudioChannelController controls the audio channel to
* play or pause.
*
* There are two ways to create an AudioChannelController instance.
* One is for System app, and the other one is for other apps.
*
* Doing `new AudioChannelController('notification')`
* to create a controller for System app's notification audio channel.
*
* Doing `new AudioChannelController(musicApp, contentChannel)`
* to create a controller for Music app's content audio channel.
*
* @param {AppWindow|String} app
* If you want to create a controller for System app's audio channel,
* use a String.
* If you want to create a controller for other app's audio channel,
* use a AppWindow.
* @param {BrowserElementAudioChannel} channel
* An audio channel belongs to the app.
*/
var AudioChannelController = function(app, channel) {
this.app = app;
this.name = channel.name;
this._channel = channel;
channel.addEventListener('activestatechanged', this);
this._states = {
active: false,
playing: false,
fadingOut: false,
vibrating: false
};
this._policy = {};
this._generateID();
};
AudioChannelController.prototype = Object.create(BaseUI.prototype);
AudioChannelController.prototype.EVENT_PREFIX = 'audiochannel';
/**
* General event handler interface.
*
* @param {Event} evt The event to handle.
*/
AudioChannelController.prototype.handleEvent = function(evt) {
switch (evt.type) {
case 'activestatechanged':
var channel = evt.target;
// TODO: We should get the `isActive` state from evt.isActive.
// Then we don't need to do `channel.isActive()` here.
channel.isActive().onsuccess = (evt) => {
this._states.active = evt.target.result;
this.publish('statechanged');
};
break;
}
};
/**
* Set the policy for handling this audio channel.
*
* @param {Object} policy Policy to handle this audio channel.
* @param {Boolean} [policy.isAllowedToPlay] Play or pause it.
* @param {Boolean} [policy.isNeededToFadeOut] Fade out or not.
* @param {Boolean} [policy.isNeededToVibrate] Vibrate or not.
* @param {Boolean} [policy.isNeededToResumeWhenOtherEnds]
* Resume when other audio channel ends if as true.
* @return {AudioChannelController}
*/
AudioChannelController.prototype.setPolicy = function(policy) {
this._policy = policy || this._policy;
return this;
};
AudioChannelController.prototype.getPolicy = function() {
return this._policy;
};
/**
* Handle the audio channel with the policy.
*
* @return {AudioChannelController}
*/
AudioChannelController.prototype.proceedPolicy = function() {
var policy = this._policy;
if (policy.isAllowedToPlay) {
this._play();
} else if (policy.isAllowedToPlay != null) {
this._pause();
}
if (policy.isNeededToFadeOut) {
this._fadeOut();
} else if (policy.isNeededToFadeOut != null) {
this._fadeIn();
}
if (policy.isNeededToVibrate) {
this._vibrate();
}
return this;
};
/**
* Get active state.
*
* @return {Boolean}
*/
AudioChannelController.prototype.isActive = function() {
return this._states.active;
};
/**
* Get state of playing.
*
* @return {Boolean}
*/
AudioChannelController.prototype.isPlaying = function() {
return this._states.playing;
};
/**
* Get state of fading out.
*
* @return {Boolean}
*/
AudioChannelController.prototype.isFadingOut = function() {
return this._states.fadingOut;
};
/**
* Get state of vibrating.
*
* @return {Boolean}
*/
AudioChannelController.prototype.isVibrating = function() {
return this._states.vibrating;
};
AudioChannelController.prototype.destroy = function() {
this.publish('destroyed');
};
/**
* Play the audio channel.
*/
AudioChannelController.prototype._play = function() {
this._states.playing = true;
var promise = new Promise((resolve) => {
var request = this._channel.setMuted(false);
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
throw 'Cannot play the audio channel.';
};
});
promise.then(() => {
this.app.debug('Play the audio channel.');
}).catch(e => {
this.app.debug(e);
});
};
/**
* Fade in the audio channel.
*/
AudioChannelController.prototype._fadeIn = function() {
this._setVolume(FADE_IN_VOLUME);
this._states.fadingOut = false;
},
/**
* Fade out the audio channel.
*/
AudioChannelController.prototype._fadeOut = function() {
this._setVolume(FADE_OUT_VOLUME);
this._states.fadingOut = true;
!this._states.playing && this._play();
},
/**
* Pause the audio channel.
*/
AudioChannelController.prototype._pause = function() {
this._states.playing = false;
var promise = new Promise((resolve) => {
var request = this._channel.setMuted(true);
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
throw 'Cannot pause the audio channel.';
};
});
promise.then(() => {
this.app.debug('Pause the audio channel');
}).catch(e => {
this.app.debug(e);
});
};
/**
* Vibrate for one second
* and the vibration pattern is [200, 100, 200, 100, ...].
*/
AudioChannelController.prototype._vibrate = function() {
var intervalId = setInterval(() => {
navigator.vibrate(200);
}, 300);
this._states.vibrating = true;
setTimeout(() => {
clearInterval(intervalId);
this._states.vibrating = false;
}, VIBRATION_DURATION);
};
/**
* Change volume of audio channels.
*
* @param {Number} volume 0 to 1.
*/
AudioChannelController.prototype._setVolume = function(volume) {
var promise = new Promise((resolve) => {
var request = this._channel.setVolume(volume);
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
throw 'Cannot set volume of the audio channel.';
};
});
promise.then(() => {
this.app.debug('Set volume: ' + volume);
}).catch(e => {
this.app.debug(e);
});
};
/**
* Generate instance ID.
*/
AudioChannelController.prototype._generateID = function() {
this.instanceID = this.app.instanceID + '_' + this.name;
};
exports.AudioChannelController = AudioChannelController;
}(window));