diff options
Diffstat (limited to 'apps/system/js/hardware_buttons.js')
-rw-r--r-- | apps/system/js/hardware_buttons.js | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/apps/system/js/hardware_buttons.js b/apps/system/js/hardware_buttons.js new file mode 100644 index 0000000..3363463 --- /dev/null +++ b/apps/system/js/hardware_buttons.js @@ -0,0 +1,318 @@ +// hardware_buttons.js: +// +// Gecko code in b2g/chrome/content/shell.js sends mozChromeEvents +// when the user presses or releases a hardware button such as Home, Sleep, +// and Volume Up and Down. +// +// This module listens for those low-level mozChromeEvents, processes them +// and generates higher-level events to handle autorepeat on the volume keys +// long presses on Home and Sleep, and the Home+Sleep key combination. +// +// Other system app modules should listen for the high-level button events +// generated by this module. +// +// The low-level input events processed by this module have type set +// to "mozChromeEvent" and detail.type set to one of: +// +// home-button-press +// home-button-release +// sleep-button-press +// sleep-button-release +// volume-up-button-press +// volume-up-button-release +// volume-down-button-press +// volume-down-button-release +// +// The high-level events generated by this module are simple Event objects +// that are not cancelable and do not bubble. The are dispatched at the +// window object. The type property is set to one of these: +// +// Event Type Meaning +// -------------------------------------------------------------- +// home short press and release of home button +// holdhome long press and hold of home button +// sleep short press and release of sleep button +// wake sleep or home pressed while sleeping +// holdsleep long press and hold of sleep button +// volumeup volume up pressed and released or autorepeated +// volumedown volume down pressed and released or autorepeated +// home+sleep home and sleep pressed at same time (used for screenshots) +// home+volume home and either volume key at the same time (view source) +// +// Because these events are fired at the window object, they cannot be +// captured. Many modules listen for the home event. Those that want +// to respond to it and prevent others from responding should call +// stopImmediatePropagation(). Overlays that want to prevent the window +// manager from showing the homescreen on the home event should call that +// method. Note, however, that this only works for scripts that run and +// register their event handlers before window_manager.js does. +// +'use strict'; + +(function() { + var HOLD_INTERVAL = 750; // How long for press and hold Home or Sleep + var REPEAT_DELAY = 700; // How long before volume autorepeat begins + var REPEAT_INTERVAL = 100; // How fast the autorepeat is. + + // Dispatch a high-level event of the specified type + function fire(type) { + window.dispatchEvent(new Event(type)); + } + + // We process events with a finite state machine. + // Each state object has a process() method for handling events. + // And optionally has enter() and exit() methods called when the FSM + // enters and exits that state + var state; + + // This function transitions to a new state + function setState(s, type) { + // Exit the current state() + if (state && state.exit) + state.exit(type); + state = s; + // Enter the new state + if (state && state.enter) + state.enter(type); + } + + // This event handler listens for hardware button events and passes the + // event type to the process() method of the current state for processing + window.addEventListener('mozChromeEvent', function(e) { + var type = e.detail.type; + switch (type) { + case 'home-button-press': + case 'home-button-release': + case 'sleep-button-press': + case 'sleep-button-release': + case 'volume-up-button-press': + case 'volume-up-button-release': + case 'volume-down-button-press': + case 'volume-down-button-release': + state.process(type); + break; + } + }); + + // The base state is the default, when no hardware buttons are pressed + var baseState = { + process: function(type) { + switch (type) { + case 'home-button-press': + // If the phone is sleeping, then pressing Home wakes it + // (on press, not release) + if (!ScreenManager.screenEnabled) { + fire('wake'); + setState(wakeState, type); + } else { + setState(homeState, type); + } + return; + case 'sleep-button-press': + // If the phone is sleeping, then pressing Sleep wakes it + // (on press, not release) + if (!ScreenManager.screenEnabled) { + fire('wake'); + setState(wakeState, type); + } else { + setState(sleepState, type); + } + return; + case 'volume-up-button-press': + case 'volume-down-button-press': + setState(volumeState, type); + return; + case 'home-button-release': + case 'sleep-button-release': + case 'volume-up-button-release': + case 'volume-down-button-release': + // Ignore button releases that occur in this state. + // These can happen after home+sleep and home+volume. + return; + } + console.error('Unexpected hardware key: ', type); + } + }; + + // We enter the home state when the user presses the Home button + // We can fire home, holdhome, or homesleep events from this state + var homeState = { + timer: null, + enter: function() { + this.timer = setTimeout(function() { + fire('holdhome'); + navigator.vibrate(50); + setState(baseState); + }, HOLD_INTERVAL); + }, + exit: function() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + }, + process: function(type) { + switch (type) { + case 'home-button-release': + fire('home'); + navigator.vibrate(50); + setState(baseState, type); + return; + case 'sleep-button-press': + fire('home+sleep'); + setState(baseState, type); + return; + case 'volume-up-button-press': + case 'volume-down-button-press': + fire('home+volume'); + setState(baseState, type); + return; + } + console.error('Unexpected hardware key: ', type); + setState(baseState, type); + } + }; + + // We enter the sleep state when the user presses the Sleep button + // We can fire sleep, holdsleep, or homesleep events from this state + var sleepState = { + timer: null, + enter: function() { + this.timer = setTimeout(function() { + fire('holdsleep'); + setState(baseState); + }, HOLD_INTERVAL); + }, + exit: function() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + }, + process: function(type) { + switch (type) { + case 'sleep-button-release': + fire('sleep'); + setState(baseState, type); + return; + case 'home-button-press': + fire('home+sleep'); + setState(baseState, type); + return; + case 'volume-up-button-press': + case 'volume-down-button-press': + setState(volumeState, type); + return; + } + console.error('Unexpected hardware key: ', type); + setState(baseState, type); + } + }; + + // We enter the volume state when the user presses the volume up or + // volume down buttons. + // We can fire volumeup and volumedown events from this state + var volumeState = { + direction: null, + timer: null, + repeating: false, + repeat: function() { + this.repeating = true; + if (this.direction === 'volume-up-button-press') + fire('volumeup'); + else + fire('volumedown'); + this.timer = setTimeout(this.repeat.bind(this), REPEAT_INTERVAL); + }, + enter: function(type) { + var self = this; + this.direction = type; // Is volume going up or down? + this.repeating = false; + this.timer = setTimeout(this.repeat.bind(this), REPEAT_DELAY); + }, + exit: function() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + }, + process: function(type) { + switch (type) { + case 'home-button-press': + fire('home+volume'); + setState(baseState, type); + return; + case 'sleep-button-press': + setState(sleepState, type); + return; + case 'volume-up-button-release': + if (this.direction === 'volume-up-button-press') { + if (!this.repeating) + fire('volumeup'); + setState(baseState, type); + return; + } + break; + case 'volume-down-button-release': + if (this.direction === 'volume-down-button-press') { + if (!this.repeating) + fire('volumedown'); + setState(baseState, type); + return; + } + break; + default: + // Ignore anything else (such as sleep button release) + return; + } + console.error('Unexpected hardware key: ', type); + setState(baseState, type); + } + }; + + // We enter this state when the user presses Home or Sleep on a sleeping + // phone. We give immediate feedback by waking the phone up on the press + // rather than waiting for the release, but this means we need a special + // state so that we don't actually send a home or sleep event on the + // key release. Note, however, that this state does set a timer so that + // it can send holdhome or holdsleep events. (This means that pressing and + // holding sleep will bring up the power menu, even on a sleeping phone.) + var wakeState = { + timer: null, + delegateState: null, + enter: function(type) { + if (type === 'home-button-press') + this.delegateState = homeState; + else + this.delegateState = sleepState; + this.timer = setTimeout(function() { + if (type === 'home-button-press') { + fire('holdhome'); + } else if (type === 'sleep-button-press') { + fire('holdsleep'); + } + setState(baseState, type); + }, HOLD_INTERVAL); + }, + exit: function() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + }, + process: function(type) { + switch (type) { + case 'home-button-release': + case 'sleep-button-release': + setState(baseState, type); + return; + default: + this.delegateState.process(type); + return; + } + } + }; + + // Kick off the FSM in the base state + setState(baseState); +}()); |