diff options
Diffstat (limited to 'apps/system/js/battery_manager.js')
-rw-r--r-- | apps/system/js/battery_manager.js | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/apps/system/js/battery_manager.js b/apps/system/js/battery_manager.js new file mode 100644 index 0000000..d2eacf8 --- /dev/null +++ b/apps/system/js/battery_manager.js @@ -0,0 +1,277 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +var BatteryManager = { + TOASTER_TIMEOUT: 5000, + TRANSITION_SPEED: 1.8, + TRANSITION_FRACTION: 0.30, + + AUTO_SHUTDOWN_LEVEL: 0.02, + EMPTY_BATTERY_LEVEL: 0.1, + + _battery: window.navigator.battery, + _notification: null, + + getAllElements: function bm_getAllElements() { + this.screen = document.getElementById('screen'); + this.overlay = document.getElementById('system-overlay'); + this.notification = document.getElementById('battery'); + }, + + checkBatteryDrainage: function bm_checkBatteryDrainage() { + var battery = this._battery; + if (!battery) + return; + + if (battery.level <= this.AUTO_SHUTDOWN_LEVEL) + SleepMenu.startPowerOff(false); + }, + + init: function bm_init() { + this.getAllElements(); + var battery = this._battery; + if (battery) { + // When the device is booted, check if the battery is drained. + // If so, SleepMenu.startPowerOff() would be called. + this.checkBatteryDrainage(); + + battery.addEventListener('levelchange', this); + battery.addEventListener('chargingchange', this); + } + window.addEventListener('screenchange', this); + this._toasterGD = new GestureDetector(this.notification); + ['mousedown', 'swipe'].forEach(function(evt) { + this.notification.addEventListener(evt, this); + }, this); + + this._screenOn = true; + this._wasEmptyBatteryNotificationDisplayed = false; + + this.displayIfNecessary(); + }, + + handleEvent: function bm_handleEvent(evt) { + switch (evt.type) { + case 'screenchange': + this._screenOn = evt.detail.screenEnabled; + this.displayIfNecessary(); + break; + + case 'levelchange': + var battery = this._battery; + if (!battery) + return; + + this.checkBatteryDrainage(); + this.displayIfNecessary(); + + PowerSaveHandler.onBatteryChange(); + break; + case 'chargingchange': + PowerSaveHandler.onBatteryChange(); + + var battery = this._battery; + // We turn the screen on if needed in order to let + // the user knows the device is charging + + if (battery && battery.charging) { + this.hide(); + this._wasEmptyBatteryNotificationDisplayed = false; + + if (!this._screenOn) { + ScreenManager.turnScreenOn(); + } + } else { + this.displayIfNecessary(); + } + break; + + case 'mousedown': + this.mousedown(evt); + break; + case 'swipe': + this.swipe(evt); + break; + } + }, + + _shouldWeDisplay: function bm_shouldWeDisplay() { + var battery = this._battery; + if (!battery) { + return false; + } + + return (!this._wasEmptyBatteryNotificationDisplayed && + !battery.charging && + battery.level <= this.EMPTY_BATTERY_LEVEL && + this._screenOn); + }, + + displayIfNecessary: function bm_display() { + if (! this._shouldWeDisplay()) { + return; + } + + // we know it's here, it's checked in shouldWeDisplay() + var level = this._battery.level; + + this.overlay.classList.add('battery'); + + this._toasterGD.startDetecting(); + this._wasEmptyBatteryNotificationDisplayed = true; + + if (this._toasterTimeout) { + clearTimeout(this._toasterTimeout); + } + + this._toasterTimeout = setTimeout(this.hide.bind(this), + this.TOASTER_TIMEOUT); + }, + + hide: function bm_hide() { + var overlayCss = this.overlay.classList; + if (overlayCss.contains('battery')) { + this.overlay.classList.remove('battery'); + this._toasterTimeout = null; + this._toasterGD.stopDetecting(); + } + }, + + // Swipe handling + mousedown: function bm_mousedown(evt) { + evt.preventDefault(); + this._containerWidth = this.overlay.clientWidth; + }, + + swipe: function bm_swipe(evt) { + var detail = evt.detail; + var distance = detail.start.screenX - detail.end.screenX; + var fastEnough = Math.abs(detail.vx) > this.TRANSITION_SPEED; + var farEnough = Math.abs(distance) > + this._containerWidth * this.TRANSITION_FRACTION; + + // If the swipe distance is too short or swipe speed is too slow, + // do nothing. + if (!(farEnough || fastEnough)) + return; + + var self = this; + this.notification.addEventListener('animationend', function animationend() { + self.notification.removeEventListener('animationend', animationend); + self.notification.classList.remove('disappearing'); + self.hide(); + }); + this.notification.classList.add('disappearing'); + } +}; + +var PowerSaveHandler = (function PowerSaveHandler() { + + var _powerSaveResume = {}; + var _powerSaveEnabled = false; + var _states = { + 'wifi.enabled' : false, + 'ril.data.enabled' : false, + 'bluetooth.enabled' : false, + 'geolocation.enabled' : false + }; + + function init() { + SettingsListener.observe('powersave.enabled', false, + function sl_getPowerSave(value) { + var enabled = value; + if (enabled) { + enablePowerSave(); + } else { + disablePowerSave(); + } + _powerSaveEnabled = enabled; + }); + + // Monitor the states of various modules + for (var j in _states) { + SettingsListener.observe(j, true, function getState(state, value) { + _states[state] = value; + }.bind(null, j)); + } + } + + // XXX Break down obj keys in a for each loop because mozSettings + // does not currently supports multiple keys in one set() + // https://bugzilla.mozilla.org/show_bug.cgi?id=779381 + function setMozSettings(keypairs) { + var setlock = SettingsListener.getSettingsLock(); + for (var key in keypairs) { + var obj = {}; + obj[key] = keypairs[key]; + setlock.set(obj); + } + } + + function enablePowerSave() { + // Keep the original states of various modules + for (var j in _states) { + _powerSaveResume[j] = _states[j]; + } + + var settingsToSet = { + // Turn off Wifi + 'wifi.enabled' : false, + // Turn off Data + 'ril.data.enabled' : false, + // Turn off Bluetooth + 'bluetooth.enabled' : false, + // Turn off Geolocation + 'geolocation.enabled' : false + }; + + setMozSettings(settingsToSet); + } + + function disablePowerSave() { + + var settingsToSet = {}; + + for (var state in _powerSaveResume) { + if (_powerSaveResume[state] == true) + settingsToSet[state] = true; + } + + setMozSettings(settingsToSet); + } + + function onBatteryChange() { + var battery = BatteryManager._battery; + + if (battery.charging) { + if (_powerSaveEnabled) + setMozSettings({'powersave.enabled' : false}); + + return; + } + + SettingsListener.observe('powersave.threshold', 0, + function getThreshold(value) { + if (battery.level <= value && !_powerSaveEnabled) { + setMozSettings({'powersave.enabled' : true}); + return; + } + + if (value != 0 && battery.level > value && _powerSaveEnabled) { + setMozSettings({'powersave.enabled' : false}); + return; + } + }); + } + + return { + init: init, + onBatteryChange: onBatteryChange + }; +})(); + +// init PowerSaveHandler first, since it will be used by BatteryManager +PowerSaveHandler.init(); +BatteryManager.init(); |