diff options
author | Daniel Narvaez <dwnarvaez@gmail.com> | 2013-02-06 13:49:14 (GMT) |
---|---|---|
committer | Daniel Narvaez <dwnarvaez@gmail.com> | 2013-02-06 13:49:14 (GMT) |
commit | 821413607a0718156f9d25d895e89b1c3d37aa8b (patch) | |
tree | 01c285af734ed5bba64b73b489e1e0226a94a262 /apps/system/js/sound_manager.js | |
parent | c110fb485b3af0066c6df7aeac8c055e9d767efa (diff) |
Copy various bits from gaia
Diffstat (limited to 'apps/system/js/sound_manager.js')
-rw-r--r-- | apps/system/js/sound_manager.js | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/apps/system/js/sound_manager.js b/apps/system/js/sound_manager.js new file mode 100644 index 0000000..52dfc06 --- /dev/null +++ b/apps/system/js/sound_manager.js @@ -0,0 +1,242 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +(function() { + window.addEventListener('volumeup', function() { + if (onBTEarphoneConnected() && onCall()) { + changeVolume(1, 'bt_sco'); + } else { + changeVolume(1); + } + }); + window.addEventListener('volumedown', function() { + if (onBTEarphoneConnected() && onCall()) { + changeVolume(-1, 'bt_sco'); + } else { + changeVolume(-1); + } + }); + + // Store the current active channel; + // change with 'audio-channel-changed' mozChromeEvent + var currentChannel = 'notification'; + + var vibrationEnabled = true; + + // This event is generated in shell.js in response to bluetooth headset. + // Bluetooth headset always assign audio volume to a specific value when + // pressing its volume-up/volume-down buttons. + window.addEventListener('mozChromeEvent', function(e) { + var type = e.detail.type; + if (type == 'bluetooth-volumeset') { + changeVolume(e.detail.value - currentVolume['bt_sco'], 'bt_sco'); + } else if (type == 'audio-channel-changed') { + currentChannel = e.detail.channel; + } + }); + + function onCall() { + if (currentChannel == 'telephony') + return true; + + // XXX: This work should be removed + // once we could get telephony channel change event + // https://bugzilla.mozilla.org/show_bug.cgi?id=819858 + var telephony = window.navigator.mozTelephony; + if (!telephony) + return false; + + return telephony.calls.some(function callIterator(call) { + return (call.state == 'connected'); + }); + } + + function onBTEarphoneConnected() { + var bluetooth = navigator.mozBluetooth; + if (!bluetooth) + return false; + + // 0x111E is for querying earphone type. + return navigator.mozBluetooth.isConnected(0x111E); + }; + + // Platform doesn't provide the maximum value of each channel + // therefore, hard code here. + var MAX_VOLUME = { + 'alarm': 15, + 'notification': 15, + 'telephony': 5, + 'content': 15, + 'bt_sco': 15 + }; + + // Please refer https://wiki.mozilla.org/WebAPI/AudioChannels > Settings + var currentVolume = { + 'alarm': 15, + 'notification': 15, + 'telephony': 5, + 'content': 15, + 'bt_sco': 15 + }; + var pendingRequestCount = 0; + + // We have three virtual states here: + // OFF -> VIBRATION -> MUTE + var muteState = 'OFF'; + + for (var channel in currentVolume) { + (function(channel) { + var setting = 'audio.volume.' + channel; + SettingsListener.observe(setting, 5, function onSettingsChange(volume) { + if (pendingRequestCount) + return; + + var max = MAX_VOLUME[channel]; + currentVolume[channel] = + parseInt(Math.max(0, Math.min(max, volume)), 10); + }); + })(channel); + } + + SettingsListener.observe('vibration.enabled', true, function(vibration) { + if (pendingRequestCount) + return; + + vibrationEnabled = vibration; + }); + + var activeTimeout = 0; + + // When hardware volume key is pressed, we need to decide which channel we + // should toggle. + // This method returns the string for setting key 'audio.volume.*' represents + // that. + // Note: this string does not always equal to currentChannel since some + // different channels are grouped together to listen to the same setting. + function getChannel() { + if (onCall()) + return 'telephony'; + + switch (currentChannel) { + case 'normal': + case 'content': + return 'content'; + case 'telephony': + return 'telephony'; + case 'alarm': + return 'alarm'; + case 'notification': + case 'ringer': + default: + return 'notification'; + } + } + + function getVolumeState(currentVolume, delta, channel) { + if (channel == 'notification') { + if (currentVolume + delta <= 0) { + if (currentVolume == 0 && vibrationEnabled) { + vibrationEnabled = false; + } else if (currentVolume > 0 && !vibrationEnabled) { + vibrationEnabled = true; + } + return 'MUTE'; + } else { + return 'OFF'; + } + } else { + if (currentVolume + delta <= 0) { + return 'MUTE'; + } else { + return 'OFF'; + } + } + } + + function changeVolume(delta, channel) { + channel = channel ? channel : getChannel(); + + muteState = getVolumeState(currentVolume[channel], delta, channel); + + var volume = currentVolume[channel] + delta; + + currentVolume[channel] = volume = + Math.max(0, Math.min(MAX_VOLUME[channel], volume)); + + var overlay = document.getElementById('system-overlay'); + var notification = document.getElementById('volume'); + var overlayClasses = overlay.classList; + var classes = notification.classList; + + switch (muteState) { + case 'OFF': + classes.remove('mute'); + if (vibrationEnabled) { + classes.add('vibration'); + } else { + classes.remove('vibration'); + } + break; + case 'MUTE': + classes.add('mute'); + if (channel == 'notification') { + if (vibrationEnabled) { + classes.add('vibration'); + SettingsListener.getSettingsLock().set({ + 'vibration.enabled': true + }); + } else { + classes.remove('vibration'); + SettingsListener.getSettingsLock().set({ + 'vibration.enabled': false + }); + } + } + break; + } + + var steps = + Array.prototype.slice.call(notification.querySelectorAll('div'), 0); + + for (var i = 0; i < steps.length; i++) { + var step = steps[i]; + if (i < volume) { + step.classList.add('active'); + } else { + step.classList.remove('active'); + } + } + + overlayClasses.add('volume'); + classes.add('visible'); + window.clearTimeout(activeTimeout); + activeTimeout = window.setTimeout(function hideSound() { + overlayClasses.remove('volume'); + classes.remove('visible'); + }, 1500); + + if (!window.navigator.mozSettings) + return; + + pendingRequestCount++; + var req; + + notification.dataset.channel = channel; + + var settingObject = {}; + settingObject['audio.volume.' + channel] = volume; + + req = SettingsListener.getSettingsLock().set(settingObject); + + req.onsuccess = function onSuccess() { + pendingRequestCount--; + }; + + req.onerror = function onError() { + pendingRequestCount--; + }; + } +})(); + |