Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/apps/system/js/screen_manager.js
diff options
context:
space:
mode:
Diffstat (limited to 'apps/system/js/screen_manager.js')
-rw-r--r--apps/system/js/screen_manager.js503
1 files changed, 503 insertions, 0 deletions
diff --git a/apps/system/js/screen_manager.js b/apps/system/js/screen_manager.js
new file mode 100644
index 0000000..9a69e8c
--- /dev/null
+++ b/apps/system/js/screen_manager.js
@@ -0,0 +1,503 @@
+/* -*- 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 ScreenManager = {
+ /*
+ * return the current screen status
+ * Must not mutate directly - use toggleScreen/turnScreenOff/turnScreenOn.
+ * Listen to 'screenchange' event to properly handle status changes
+ * This value can be "out of sync" with real mozPower value,
+ * we do this to give screen some time to flash before actual turn off.
+ */
+ screenEnabled: false,
+
+ /*
+ * before idle-screen-off, invoke a nice dimming to the brightness
+ * to notify the user that the screen is about to be turn off.
+ * The user can cancel the idle-screen-off by touching the screen
+ * and by pressing a button (trigger onactive callback on Idle API)
+ *
+ */
+ _inTransition: false,
+
+ /*
+ * Whether the wake lock is enabled or not
+ */
+ _screenWakeLocked: false,
+
+ /*
+ * Whether the device light is enabled or not
+ * sync with setting 'screen.automatic-brightness'
+ */
+ _deviceLightEnabled: true,
+
+ /*
+ * Preferred brightness without considering device light nor dimming
+ * sync with setting 'screen.brightness'
+ */
+ _userBrightness: 1,
+ _savedBrightness: 1,
+
+ /*
+ * The auto-brightness algorithm will never set the screen brightness
+ * to a value smaller than this. 0.1 seems like a good screen brightness
+ * in a completely dark room on a Unagi.
+ */
+ AUTO_BRIGHTNESS_MINIMUM: 0.1,
+
+ /*
+ * This constant is used in the auto brightness algorithm. We take
+ * the base 10 logarithm of the incoming lux value from the light
+ * sensor and multiplied it by this constant. That value is used to
+ * compute a weighted average with the current brightness and
+ * finally that average brightess is and then clamped to the range
+ * [AUTO_BRIGHTNESS_MINIMUM, 1.0].
+ *
+ * Making this value larger will increase the brightness for a given
+ * ambient light level. At a value of about .25, the screen will be
+ * at full brightness in sunlight or in a well-lighted work area.
+ * At a value of about .3, the screen will typically be at maximum
+ * brightness in outdoor daylight conditions, even when overcast.
+ */
+ AUTO_BRIGHTNESS_CONSTANT: .27,
+
+ /*
+ * When we change brightness we animate it smoothly.
+ * This constant is the number of milliseconds between adjustments
+ */
+ BRIGHTNESS_ADJUST_INTERVAL: 20,
+
+ /*
+ * When brightening or dimming the screen, this is how much we adjust
+ * the brightness value at a time.
+ */
+ BRIGHTNESS_ADJUST_STEP: 0.04,
+
+ /*
+ * Wait for _dimNotice milliseconds during idle-screen-off
+ */
+ _dimNotice: 10 * 1000,
+
+ /*
+ * We track the value of the idle timeout pref in this variable.
+ */
+ _idleTimeout: 0,
+ _idleTimerId: 0,
+
+ /*
+ * If the screen off is triggered by promixity during phon call then
+ * we need wake it up while phone is ended.
+ */
+ _screenOffByProximity: false,
+
+ /*
+ * Request wakelock during in_call state.
+ * To ensure turnScreenOff by proximity event is protected by wakelock for
+ * early suspend only.
+ */
+ _cpuWakeLock: null,
+
+ init: function scm_init() {
+ window.addEventListener('sleep', this);
+ window.addEventListener('wake', this);
+
+ this.screen = document.getElementById('screen');
+
+ var self = this;
+ var power = navigator.mozPower;
+
+ if (power) {
+ power.addWakeLockListener(function scm_handleWakeLock(topic, state) {
+ switch (topic) {
+ case 'screen':
+ self._screenWakeLocked = (state == 'locked-foreground');
+
+ if (self._screenWakeLocked)
+ // Turn screen on if wake lock is acquire
+ self.turnScreenOn();
+ self._reconfigScreenTimeout();
+ break;
+
+ case 'cpu':
+ power.cpuSleepAllowed = (state != 'locked-foreground' &&
+ state != 'locked-background');
+ break;
+ }
+ });
+ }
+
+ this._firstOn = false;
+ SettingsListener.observe('screen.timeout', 60,
+ function screenTimeoutChanged(value) {
+ if (typeof value !== 'number')
+ value = parseInt(value);
+ self._idleTimeout = value;
+ self._setIdleTimeout(self._idleTimeout);
+
+ if (!self._firstOn) {
+ self._firstOn = true;
+
+ // During boot up, the brightness was set by bootloader as 0.5,
+ // Let's set the API value to that so setScreenBrightness() can
+ // dim nicely to value set by user.
+ power.screenBrightness = 0.5;
+
+ // Turn screen on with dim.
+ self.turnScreenOn(false);
+ }
+ });
+
+ SettingsListener.observe('screen.automatic-brightness', true,
+ function deviceLightSettingChanged(value) {
+ self.setDeviceLightEnabled(value);
+ });
+
+ SettingsListener.observe('screen.brightness', 1,
+ function brightnessSettingChanged(value) {
+ self._userBrightness = value;
+ self.setScreenBrightness(value, false);
+ });
+
+ var telephony = window.navigator.mozTelephony;
+ if (telephony) {
+ telephony.addEventListener('callschanged', this);
+ }
+ },
+
+ //
+ // Automatically adjust the screen brightness based on the ambient
+ // light (in lux) measured by the device light sensor
+ //
+ autoAdjustBrightness: function scm_adjustBrightness(lux) {
+ var currentBrightness = this._targetBrightness;
+
+ if (lux < 1) // Can't take the log of 0 or negative numbers
+ lux = 1;
+
+ var computedBrightness =
+ Math.log(lux) / Math.LN10 * this.AUTO_BRIGHTNESS_CONSTANT;
+
+ var clampedBrightness = Math.max(this.AUTO_BRIGHTNESS_MINIMUM,
+ Math.min(1.0, computedBrightness));
+
+ // If nothing changed, we're done.
+ if (clampedBrightness === currentBrightness)
+ return;
+
+ this.setScreenBrightness(clampedBrightness, false);
+ },
+
+ handleEvent: function scm_handleEvent(evt) {
+ switch (evt.type) {
+ case 'devicelight':
+ if (!this._deviceLightEnabled || !this.screenEnabled ||
+ this._inTransition)
+ return;
+ this.autoAdjustBrightness(evt.value);
+ break;
+
+ case 'sleep':
+ this.turnScreenOff(true);
+ break;
+
+ case 'wake':
+ this.turnScreenOn();
+ break;
+
+ case 'userproximity':
+ this._screenOffByProximity = evt.near;
+ if (evt.near) {
+ this.turnScreenOff(true);
+ } else {
+ this.turnScreenOn();
+ }
+ break;
+
+ case 'callschanged':
+ var telephony = window.navigator.mozTelephony;
+ if (!telephony.calls.length) {
+ if (this._screenOffByProximity) {
+ this.turnScreenOn();
+ }
+
+ window.removeEventListener('userproximity', this);
+ this._screenOffByProximity = false;
+
+ if (this._cpuWakeLock) {
+ this._cpuWakeLock.unlock();
+ this._cpuWakeLock = null;
+ }
+ break;
+ }
+
+ // If the _cpuWakeLock is already set we are in a multiple
+ // call setup, turning the screen on to let user see the
+ // notification.
+ if (this._cpuWakeLock) {
+ this.turnScreenOn();
+
+ break;
+ }
+
+ // Enable the user proximity sensor once the call is connected.
+ var call = telephony.calls[0];
+ call.addEventListener('statechange', this);
+
+ break;
+
+ case 'statechange':
+ var call = evt.target;
+ if (call.state !== 'connected') {
+ break;
+ }
+
+ // The call is connected. Remove the statechange listener
+ // and enable the user proximity sensor.
+ call.removeEventListener('statechange', this);
+
+ this._cpuWakeLock = navigator.requestWakeLock('cpu');
+ window.addEventListener('userproximity', this);
+ break;
+ }
+ },
+
+ toggleScreen: function scm_toggleScreen() {
+ if (this.screenEnabled) {
+ this.turnScreenOff();
+ } else {
+ this.turnScreenOn();
+ }
+ },
+
+ turnScreenOff: function scm_turnScreenOff(instant) {
+ if (!this.screenEnabled)
+ return false;
+
+ var self = this;
+
+ // Remember the current screen brightness. We will restore it when
+ // we turn the screen back on.
+ self._savedBrightness = navigator.mozPower.screenBrightness;
+
+ // Remove the cpuWakeLock if screen is not turned off by
+ // userproximity event.
+ if (!this._screenOffByProximity && this._cpuWakeLock) {
+ window.removeEventListener('userproximity', this);
+ this._cpuWakeLock.unlock();
+ this._cpuWakeLock = null;
+ }
+
+ var screenOff = function scm_screenOff() {
+ self._setIdleTimeout(0);
+
+ window.removeEventListener('devicelight', self);
+
+ self.screenEnabled = false;
+ self._inTransition = false;
+ self.screen.classList.add('screenoff');
+ setTimeout(function realScreenOff() {
+ self.setScreenBrightness(0, true);
+ // Sometimes the ScreenManager.screenEnabled and mozPower.screenEnabled
+ // values are out of sync. Since the rest of the world relies only on
+ // the value of ScreenManager.screenEnabled it can be some situations
+ // where the screen is off but ScreenManager think it is on... (see
+ // bug 822463). Ideally a callback should have been used, like
+ // ScreenManager.getScreenState(function(value) { ...} ); but there
+ // are too many places to change that for now.
+ self.screenEnabled = false;
+ navigator.mozPower.screenEnabled = false;
+ }, 20);
+
+ self.fireScreenChangeEvent();
+ };
+
+ if (instant) {
+ if (!WindowManager.isFtuRunning()) {
+ screenOff();
+ }
+ return true;
+ }
+
+ this.setScreenBrightness(0.1, false);
+ this._inTransition = true;
+ setTimeout(function noticeTimeout() {
+ if (!self._inTransition)
+ return;
+
+ screenOff();
+ }, self._dimNotice);
+
+ return true;
+ },
+
+ turnScreenOn: function scm_turnScreenOn(instant) {
+ if (this.screenEnabled) {
+ if (this._inTransition) {
+ // Cancel the dim out
+ this._inTransition = false;
+ this.setScreenBrightness(this._savedBrightness, true);
+ this._reconfigScreenTimeout();
+ }
+ return false;
+ }
+
+ // Set the brightness before the screen is on.
+ this.setScreenBrightness(this._savedBrightness, instant);
+
+ // If we are in a call and there is no cpuWakeLock,
+ // we would have to get one here.
+ var telephony = window.navigator.mozTelephony;
+ if (!this._cpuWakeLock && telephony && telephony.calls.length) {
+ telephony.calls.some(function checkCallConnection(call) {
+ if (call.state == 'connected') {
+ this._cpuWakeLock = navigator.requestWakeLock('cpu');
+ window.addEventListener('userproximity', this);
+ return true;
+ }
+ return false;
+ }, this);
+ }
+
+ // Actually turn the screen on.
+ var power = navigator.mozPower;
+ if (power)
+ power.screenEnabled = true;
+ this.screenEnabled = true;
+ this.screen.classList.remove('screenoff');
+
+ // Attaching the event listener effectively turn on the hardware
+ // device light sensor, which _must be_ done after power.screenEnabled.
+ if (this._deviceLightEnabled)
+ window.addEventListener('devicelight', this);
+
+ this._reconfigScreenTimeout();
+ this.fireScreenChangeEvent();
+
+ return true;
+ },
+
+ _reconfigScreenTimeout: function scm_reconfigScreenTimeout() {
+ // Remove idle timer if screen wake lock is acquired.
+ if (this._screenWakeLocked) {
+ this._setIdleTimeout(0);
+ // The screen should be turn off with shorter timeout if
+ // it was never unlocked.
+ } else if (LockScreen.locked) {
+ this._setIdleTimeout(10, true);
+ var self = this;
+ var stopShortIdleTimeout = function scm_stopShortIdleTimeout() {
+ window.removeEventListener('unlock', stopShortIdleTimeout);
+ window.removeEventListener('lockpanelchange', stopShortIdleTimeout);
+ self._setIdleTimeout(self._idleTimeout, false);
+ };
+
+ window.addEventListener('unlock', stopShortIdleTimeout);
+ window.addEventListener('lockpanelchange', stopShortIdleTimeout);
+ } else {
+ this._setIdleTimeout(this._idleTimeout, false);
+ }
+ },
+
+ setScreenBrightness: function scm_setScreenBrightness(brightness, instant) {
+ this._targetBrightness = brightness;
+ var power = navigator.mozPower;
+ if (!power)
+ return;
+
+ // Make sure we don't have another brightness change scheduled
+ if (this._transitionBrightnessTimer) {
+ clearTimeout(this._transitionBrightnessTimer);
+ this._transitionBrightnessTimer = null;
+ }
+
+ if (typeof instant !== 'boolean')
+ instant = true;
+
+ if (instant) {
+ power.screenBrightness = brightness;
+ return;
+ }
+
+ // transitionBrightness() is a looping function that will
+ // gracefully tune the brightness to _targetBrightness for us.
+ this.transitionBrightness();
+ },
+
+ transitionBrightness: function scm_transitionBrightness() {
+ var self = this;
+ var power = navigator.mozPower;
+ var screenBrightness = power.screenBrightness;
+ var delta = this.BRIGHTNESS_ADJUST_STEP;
+
+ // Is this the last time adjustment we need to make?
+ if (Math.abs(this._targetBrightness - screenBrightness) <= delta) {
+ power.screenBrightness = this._targetBrightness;
+ this._transitionBrightnessTimer = null;
+ return;
+ }
+
+ if (screenBrightness > this._targetBrightness)
+ delta *= -1;
+
+ screenBrightness += delta;
+ power.screenBrightness = screenBrightness;
+
+ this._transitionBrightnessTimer =
+ setTimeout(function transitionBrightnessTimeout() {
+ self.transitionBrightness();
+ }, this.BRIGHTNESS_ADJUST_INTERVAL);
+ },
+
+ setDeviceLightEnabled: function scm_setDeviceLightEnabled(enabled) {
+ if (!enabled && this._deviceLightEnabled) {
+ // Disabled -- set the brightness back to preferred brightness
+ this.setScreenBrightness(this._userBrightness, false);
+ }
+ this._deviceLightEnabled = enabled;
+
+ if (!this.screenEnabled)
+ return;
+
+ // Disable/enable device light sensor accordingly.
+ // This will also toggle the actual hardware, which
+ // must be done while the screen is on.
+ if (enabled) {
+ window.addEventListener('devicelight', this);
+ } else {
+ window.removeEventListener('devicelight', this);
+ }
+ },
+
+ _setIdleTimeout: function scm_setIdleTimeout(time, instant) {
+ window.clearIdleTimeout(this._idleTimerId);
+
+ // Reset the idled state back to false.
+ this._idled = false;
+
+ // 0 is the value used to disable idle timer by user and by us.
+ if (time === 0)
+ return;
+
+ var self = this;
+ var idleCallback = function idle_proxy() {
+ self.turnScreenOff(instant);
+ };
+ var activeCallback = function active_proxy() {
+ self.turnScreenOn(true);
+ };
+
+ this._idleTimerId = window.setIdleTimeout(idleCallback,
+ activeCallback, time * 1000);
+ },
+
+ fireScreenChangeEvent: function scm_fireScreenChangeEvent() {
+ var evt = new CustomEvent('screenchange',
+ { bubbles: true, cancelable: false,
+ detail: { screenEnabled: this.screenEnabled } });
+ window.dispatchEvent(evt);
+ }
+};
+
+ScreenManager.init();