Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
686 lines
22 KiB
JavaScript
686 lines
22 KiB
JavaScript
'use strict';
|
|
|
|
/* globals SettingsListener, Service,
|
|
ScreenBrightnessTransition, ScreenWakeLockManager,
|
|
ScreenAutoBrightness, LazyLoader */
|
|
|
|
(function(exports) {
|
|
var ScreenManager = {
|
|
name: '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,
|
|
|
|
/**
|
|
* If user is unlocking, postpone the timeout counter.
|
|
*/
|
|
_unlocking: 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 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,
|
|
|
|
/*
|
|
* This property will host a ScreenBrightnessTransition instance
|
|
* and control the brightness transition for us.
|
|
* Eventually we want to move all brightness controls
|
|
* (including auto-brightness toggle and calculation) out of this module.
|
|
*/
|
|
_screenBrightnessTransition: null,
|
|
|
|
/*
|
|
* ScreenAutoBrightness instance
|
|
* manages the devicelight events and adjusts the screen brightness
|
|
* automatically
|
|
*/
|
|
_screenAutoBrightness: null,
|
|
|
|
/**
|
|
* Timeout to black the screen when locking.
|
|
*/
|
|
LOCKING_TIMEOUT: 10,
|
|
|
|
/*
|
|
* 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,
|
|
|
|
/*
|
|
* To track the reason caused screen off?
|
|
*/
|
|
_screenOffBy: null,
|
|
_screenOffTimeout: 0,
|
|
|
|
/*
|
|
* Request wakelock during in_call state.
|
|
* To ensure turnScreenOff by proximity event is protected by wakelock for
|
|
* early suspend only.
|
|
*/
|
|
_cpuWakeLock: null,
|
|
|
|
/*
|
|
* A screen reader feature to allow screen brightness to remain 0
|
|
* regardless of the enabled state of the screen. This allows the user to
|
|
* conserve battery and have more privacy while still using the touchscreen.
|
|
*/
|
|
_screenShade: false,
|
|
|
|
start: function() {
|
|
window.addEventListener('attentionopening', this);
|
|
window.addEventListener('attentionopened', this);
|
|
window.addEventListener('sleep', this);
|
|
window.addEventListener('wake', this);
|
|
window.addEventListener('nfc-tech-discovered', this);
|
|
window.addEventListener('nfc-tech-lost', this);
|
|
window.addEventListener('requestshutdown', this);
|
|
|
|
// User is unlocking by sliding or other methods.
|
|
window.addEventListener('unlocking-start', this);
|
|
window.addEventListener('unlocking-stop', this);
|
|
|
|
// When secure app is on, do not turn the screen off.
|
|
// And when it's down, reset the timeout.
|
|
window.addEventListener('secure-appopened', this);
|
|
window.addEventListener('secure-appterminated', this);
|
|
|
|
window.addEventListener('logohidden', this);
|
|
|
|
// User is actively using the screen reader.
|
|
window.addEventListener('accessibility-action', this);
|
|
|
|
this.screen = document.getElementById('screen');
|
|
|
|
LazyLoader.load([
|
|
'js/screen_auto_brightness.js',
|
|
'js/screen_brightness_transition.js'
|
|
]).then(function() {
|
|
this._screenBrightnessTransition = new ScreenBrightnessTransition();
|
|
|
|
this._screenAutoBrightness = new ScreenAutoBrightness();
|
|
this._screenAutoBrightness.onbrightnesschange = function(brightness) {
|
|
this.setScreenBrightness(brightness, true);
|
|
}.bind(this);
|
|
|
|
this._screenBrightnessTransition.ontransitionbegin =
|
|
this._screenAutoBrightness.pause.bind(this._screenAutoBrightness);
|
|
this._screenBrightnessTransition.ontransitionend =
|
|
this._screenAutoBrightness.resume.bind(this._screenAutoBrightness);
|
|
|
|
}.bind(this))['catch'](function(err) {
|
|
console.error(err);
|
|
});
|
|
|
|
var self = this;
|
|
var power = navigator.mozPower;
|
|
|
|
// Start the screen wake lock manager so it will monitor screen wake lock
|
|
// for us. We will need to re-config the screen timeout
|
|
// when the lock state is changed.
|
|
//
|
|
// Noted that getting a lock while the screen is off will not
|
|
// turn on the screen, since no frame is considered visible by Gecko when
|
|
// the screen is off. See discussion in bug 818840.
|
|
this._wakeLockManager = new ScreenWakeLockManager();
|
|
this._wakeLockManager.onwakelockchange =
|
|
this._reconfigScreenTimeout.bind(this);
|
|
this._wakeLockManager.start();
|
|
|
|
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);
|
|
}
|
|
Service.registerState('screenEnabled', this);
|
|
Service.register('turnScreenOn', this);
|
|
Service.register('turnScreenOff', this);
|
|
Service.register('turnShadeOn', this);
|
|
Service.register('turnShadeOff', this);
|
|
},
|
|
|
|
_getNumOfCalls: function scm_getNumOfCalls() {
|
|
if (navigator.mozTelephony) {
|
|
return navigator.mozTelephony.calls.length +
|
|
(navigator.mozTelephony.conferenceGroup.calls.length ? 1 : 0);
|
|
} else {
|
|
return 0;
|
|
}
|
|
},
|
|
|
|
handleEvent: function scm_handleEvent(evt) {
|
|
var telephony = window.navigator.mozTelephony;
|
|
var call;
|
|
|
|
switch (evt.type) {
|
|
case 'attentionopening':
|
|
case 'attentionopened':
|
|
if (!this.enabled) {
|
|
this.turnScreenOn();
|
|
}
|
|
break;
|
|
case 'devicelight':
|
|
if (!this._deviceLightEnabled || !this.screenEnabled ||
|
|
this._inTransition) {
|
|
return;
|
|
}
|
|
this._screenAutoBrightness &&
|
|
this._screenAutoBrightness.autoAdjust(evt.value);
|
|
break;
|
|
|
|
case 'sleep':
|
|
// If calls are present then the power button will be handled by the
|
|
// dialer agent.
|
|
if (this._getNumOfCalls() === 0) {
|
|
this.turnScreenOff(true, 'powerkey');
|
|
}
|
|
|
|
break;
|
|
|
|
case 'wake':
|
|
// If calls are present then the power button will be handled by the
|
|
// dialer agent.
|
|
if (this._getNumOfCalls() === 0) {
|
|
this.turnScreenOn();
|
|
}
|
|
|
|
break;
|
|
|
|
case 'accessibility-action':
|
|
this._reconfigScreenTimeout();
|
|
break;
|
|
|
|
case 'nfc-tech-discovered':
|
|
case 'nfc-tech-lost':
|
|
if (this._inTransition) {
|
|
this.turnScreenOn();
|
|
} else {
|
|
this._reconfigScreenTimeout();
|
|
}
|
|
break;
|
|
|
|
case 'unlocking-start':
|
|
this._setUnlocking();
|
|
break;
|
|
|
|
// When secure app is on, do not turn the screen off.
|
|
// And when it's down, reset the timeout.
|
|
case 'secure-appopened':
|
|
case 'secure-appterminated':
|
|
this._reconfigScreenTimeout();
|
|
break;
|
|
case 'unlocking-stop':
|
|
this._resetUnlocking();
|
|
break;
|
|
|
|
case 'userproximity':
|
|
if (Service.query('Bluetooth.isSCOProfileConnected') ||
|
|
telephony.speakerEnabled ||
|
|
Service.query('isHeadsetConnected')) {
|
|
// XXX: Remove this hack in Bug 868348
|
|
// We shouldn't access headset status from statusbar.
|
|
if (this._screenOffBy == 'proximity') {
|
|
this.turnScreenOn();
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (evt.near) {
|
|
this.turnScreenOff(true, 'proximity');
|
|
} else {
|
|
this.turnScreenOn();
|
|
}
|
|
break;
|
|
|
|
case 'callschanged':
|
|
if (this._getNumOfCalls() === 0) {
|
|
this._uninstallProximityListener();
|
|
break;
|
|
}
|
|
|
|
// If the _cpuWakeLock is already set we are in a multiple
|
|
// call setup, the user will be notified by a tone.
|
|
if (this._cpuWakeLock) {
|
|
break;
|
|
}
|
|
|
|
// Enable the user proximity sensor once the call is connected.
|
|
call = telephony.calls[0];
|
|
if (call.state === 'dialing') {
|
|
this._installProximityListener();
|
|
} else {
|
|
call.addEventListener('statechange', this);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'statechange':
|
|
call = evt.target;
|
|
if (['connected', 'alerting'].indexOf(call.state) === -1) {
|
|
break;
|
|
}
|
|
|
|
// The call is connected (MT call) or alerting/dialing (MO call).
|
|
// Remove the statechange listener and enable the user proximity
|
|
// sensor.
|
|
call.removeEventListener('statechange', this);
|
|
|
|
this._installProximityListener();
|
|
break;
|
|
|
|
// Reconfig screen time out after booting.
|
|
case 'logohidden':
|
|
window.removeEventListener('logohidden', this);
|
|
this._reconfigScreenTimeout();
|
|
break;
|
|
|
|
case 'lockscreen-appclosing' :
|
|
case 'lockpanelchange' :
|
|
window.removeEventListener('lockscreen-appclosing', this);
|
|
window.removeEventListener('lockpanelchange', this);
|
|
// Prevent racing: wakeLockManager may broadcast the message
|
|
// before this event, so one lockscreen event is actually representing
|
|
// different meanings depends on that.
|
|
//
|
|
// This happens partly because this handler doesn't enter the
|
|
// '_reconfigScreenTimeout' as other handlers may do, but in fact
|
|
// the centralized design of putting all different timeouts in one
|
|
// management function is not a good idea, because that fuzzes the
|
|
// real intention of each branch. So maybe a more complete solution
|
|
// is to decentralize that function, and make every small timeout
|
|
// configuring functions more clear.
|
|
//
|
|
// Another reason why just to set the timeout is bad because it
|
|
// depends on the event order heavily without checking any status
|
|
// at the moment the event comes. To perform such check can prevent
|
|
// that, too.
|
|
if (!this._wakeLockManager.isHeld) {
|
|
this._setIdleTimeout(this._idleTimeout, false);
|
|
}
|
|
break;
|
|
|
|
case 'requestshutdown':
|
|
this.turnScreenOn();
|
|
if (evt.detail && evt.detail.startPowerOff) {
|
|
evt.detail.startPowerOff(false);
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
|
|
toggleScreen: function scm_toggleScreen() {
|
|
if (this.screenEnabled) {
|
|
// Currently there is no one used toggleScreen, so just set reason as
|
|
// toggle. If it is used by someone in the future, we can rename it.
|
|
this.turnScreenOff(true, 'toggle');
|
|
} else {
|
|
this.turnScreenOn();
|
|
}
|
|
},
|
|
|
|
turnShadeOn: function scm_turnShadeOn() {
|
|
this._screenShade = true;
|
|
// Remember the current screen brightness. We will restore it when
|
|
// we turn the shade back on.
|
|
this._savedBrightness = navigator.mozPower.screenBrightness;
|
|
this.setScreenBrightness(0, true);
|
|
},
|
|
|
|
turnShadeOff: function scm_turnShadeOff() {
|
|
// Bug 1185036: If the screen was disable with the shade on, we won't
|
|
// the screen won't turn on again until the user puts their finger on the
|
|
// screen again.
|
|
var brightness = this._savedBrightness || this._userBrightness || 1;
|
|
this._screenShade = false;
|
|
this.setScreenBrightness(brightness, true);
|
|
},
|
|
|
|
turnScreenOff: function scm_turnScreenOff(instant, reason) {
|
|
if (!this.screenEnabled) {
|
|
return false;
|
|
}
|
|
|
|
var self = this;
|
|
if (reason) {
|
|
this._screenOffBy = reason;
|
|
}
|
|
|
|
// Remember the current screen brightness. We will restore it when
|
|
// we turn the screen back on.
|
|
self._savedBrightness = navigator.mozPower.screenBrightness;
|
|
|
|
// Remove the cpuWakeLock and listening of proximity event, if screen is
|
|
// turned off by power key.
|
|
if (this._cpuWakeLock != null && this._screenOffBy == 'powerkey') {
|
|
window.removeEventListener('userproximity', this);
|
|
this._cpuWakeLock.unlock();
|
|
this._cpuWakeLock = null;
|
|
}
|
|
|
|
var screenOff = function scm_screenOff() {
|
|
self._setIdleTimeout(0);
|
|
|
|
if (self._deviceLightEnabled) {
|
|
window.removeEventListener('devicelight', self);
|
|
}
|
|
|
|
window.removeEventListener('lockscreen-appclosing', self);
|
|
window.removeEventListener('lockpanelchange', self);
|
|
self.screenEnabled = false;
|
|
self._inTransition = false;
|
|
clearTimeout(self._screenOffTimeout);
|
|
self._screenOffTimeout = 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;
|
|
navigator.mozPower.keyLightEnabled = false;
|
|
self._screenAutoBrightness && self._screenAutoBrightness.pause();
|
|
}, 20);
|
|
|
|
self.fireScreenChangeEvent();
|
|
};
|
|
|
|
if (instant) {
|
|
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) {
|
|
clearTimeout(this._screenOffTimeout);
|
|
if (this.screenEnabled) {
|
|
if (this._inTransition) {
|
|
// Cancel the dim out
|
|
this._inTransition = false;
|
|
this.setScreenBrightness(this._savedBrightness, true);
|
|
this._reconfigScreenTimeout();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Actually turn the screen on.
|
|
var power = navigator.mozPower;
|
|
if (power) {
|
|
power.screenEnabled = true;
|
|
power.keyLightEnabled = true;
|
|
}
|
|
this.screenEnabled = true;
|
|
|
|
// Set the brightness before the screen is on.
|
|
this.setScreenBrightness(this._savedBrightness, instant);
|
|
|
|
// Resume auto brightness, which is paused when the screen is turned off.
|
|
this._screenAutoBrightness && this._screenAutoBrightness.resume();
|
|
|
|
// If we are in a call or a conference call and there
|
|
// is no cpuWakeLock, we would get one here.
|
|
var telephony = window.navigator.mozTelephony;
|
|
var ongoingConference = telephony && telephony.conferenceGroup &&
|
|
telephony.conferenceGroup.calls.length;
|
|
if (!this._cpuWakeLock && telephony &&
|
|
(telephony.calls.length || ongoingConference)) {
|
|
|
|
var connected = telephony.calls.some(function(call) {
|
|
if (call.state == 'connected') {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
if (connected || ongoingConference) {
|
|
this._cpuWakeLock = navigator.requestWakeLock('cpu');
|
|
window.addEventListener('userproximity', this);
|
|
}
|
|
}
|
|
|
|
// 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 or
|
|
// if no app has been displayed yet.
|
|
if (this._wakeLockManager.isHeld ||
|
|
(!Service.query('AppWindowManager.getActiveWindow') &&
|
|
!Service.query('locked'))) {
|
|
this._setIdleTimeout(0);
|
|
// The screen should be turn off with shorter timeout if
|
|
// it was never unlocked.
|
|
} else if (!this._unlocking) {
|
|
if (Service.query('getTopMostWindow') &&
|
|
Service.query('getTopMostWindow').CLASS_NAME ===
|
|
'LockScreenWindow') {
|
|
this._setIdleTimeout(this.LOCKING_TIMEOUT, true);
|
|
window.addEventListener('lockscreen-appclosing', this);
|
|
window.addEventListener('lockpanelchange', this);
|
|
} else {
|
|
this._setIdleTimeout(this._idleTimeout, false);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* If user is unlocking, postpone the timeout counter.
|
|
*
|
|
* @this {ScreenManager}
|
|
*/
|
|
_setUnlocking: function scm_setUnlocking() {
|
|
this._unlocking = true;
|
|
|
|
// Need to cancel it: the last set timeout would still be triggered.
|
|
window.clearIdleTimeout(this._idleTimerId);
|
|
},
|
|
|
|
/**
|
|
* Reset the state of user unlocking.
|
|
*
|
|
* @this {ScreenManager}
|
|
*/
|
|
_resetUnlocking: function scm_resetUnlocking() {
|
|
this._unlocking = false;
|
|
this._reconfigScreenTimeout();
|
|
},
|
|
|
|
setScreenBrightness: function scm_setScreenBrightness(brightness, instant) {
|
|
// Don't touch the brightness if the screen shade is on.
|
|
if (this._screenShade && !!brightness) {
|
|
return;
|
|
}
|
|
|
|
this._targetBrightness = brightness;
|
|
var power = navigator.mozPower;
|
|
if (!power) {
|
|
return;
|
|
}
|
|
|
|
// Stop the current transition
|
|
if (this._screenBrightnessTransition &&
|
|
this._screenBrightnessTransition.isRunning) {
|
|
this._screenBrightnessTransition.abort();
|
|
}
|
|
|
|
if (typeof instant !== 'boolean') {
|
|
instant = true;
|
|
}
|
|
|
|
if (instant) {
|
|
power.screenBrightness = brightness;
|
|
return;
|
|
}
|
|
|
|
if (this._screenBrightnessTransition) {
|
|
this._screenBrightnessTransition.transitionTo(this._targetBrightness);
|
|
} else {
|
|
power.screenBrightness = this._targetBrightness;
|
|
}
|
|
},
|
|
|
|
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;
|
|
|
|
this._screenAutoBrightness &&
|
|
this._screenAutoBrightness.reset();
|
|
|
|
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, 'idle_timeout');
|
|
};
|
|
var activeCallback = function active_proxy() {
|
|
self.turnScreenOn(true);
|
|
};
|
|
|
|
var finalTimeout =
|
|
instant ? time * 1000 : (time * 1000) - this._dimNotice;
|
|
this._idleTimerId = window.setIdleTimeout(idleCallback,
|
|
activeCallback, finalTimeout);
|
|
},
|
|
|
|
fireScreenChangeEvent: function scm_fireScreenChangeEvent() {
|
|
var detail = { screenEnabled: this.screenEnabled };
|
|
|
|
// Tell others the cause of screen-off.
|
|
detail.screenOffBy = this._screenOffBy;
|
|
|
|
var evt = new CustomEvent('screenchange',
|
|
{ bubbles: true, cancelable: false,
|
|
detail: detail });
|
|
window.dispatchEvent(evt);
|
|
},
|
|
|
|
_installProximityListener: function() {
|
|
if (this._cpuWakeLock) {
|
|
return;
|
|
}
|
|
this._cpuWakeLock = navigator.requestWakeLock('cpu');
|
|
window.addEventListener('userproximity', this);
|
|
},
|
|
|
|
_uninstallProximityListener: function() {
|
|
window.removeEventListener('userproximity', this);
|
|
|
|
if (this._cpuWakeLock) {
|
|
this._cpuWakeLock.unlock();
|
|
this._cpuWakeLock = null;
|
|
}
|
|
}
|
|
};
|
|
exports.ScreenManager = ScreenManager;
|
|
}(window));
|
|
|