/* 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));