Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/apps/system/js/hardware_buttons.js
diff options
context:
space:
mode:
Diffstat (limited to 'apps/system/js/hardware_buttons.js')
-rw-r--r--apps/system/js/hardware_buttons.js318
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);
+}());