text: // 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);
  }());
  