@Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    switch (keyCode) {
      case KeyEvent.KEYCODE_MENU:
        if (Receiver.call_state == UserAgent.UA_STATE_INCOMING_CALL
            && mSlidingCardManager == null) {
          answer();
          return true;
        }
        break;

      case KeyEvent.KEYCODE_CALL:
        switch (Receiver.call_state) {
          case UserAgent.UA_STATE_INCOMING_CALL:
            answer();
            break;
          case UserAgent.UA_STATE_INCALL:
          case UserAgent.UA_STATE_HOLD:
            Receiver.engine(this).togglehold();
            break;
        }
        // consume KEYCODE_CALL so PhoneWindow doesn't do anything with it
        return true;

      case KeyEvent.KEYCODE_BACK:
        if (mDialerDrawer.isOpened()) mDialerDrawer.animateClose();
        else if (Receiver.call_state == UserAgent.UA_STATE_INCOMING_CALL) reject();
        return true;

      case KeyEvent.KEYCODE_CAMERA:
        // Disable the CAMERA button while in-call since it's too
        // easy to press accidentally.
        return true;

      case KeyEvent.KEYCODE_VOLUME_DOWN:
      case KeyEvent.KEYCODE_VOLUME_UP:
        if (Receiver.call_state == UserAgent.UA_STATE_INCOMING_CALL) {
          Receiver.stopRingtone();
          return true;
        }
        RtpStreamReceiver.adjust(keyCode, true);
        return true;
    }
    if (Receiver.call_state == UserAgent.UA_STATE_INCALL) {
      char number = event.getNumber();
      if (Character.isDigit(number) || number == '*' || number == '#') {
        appendDigit(number);
        return true;
      }
    }
    return super.onKeyDown(keyCode, event);
  }
 @Override
 public boolean onKeyUp(int keyCode, KeyEvent event) {
   switch (keyCode) {
     case KeyEvent.KEYCODE_VOLUME_DOWN:
     case KeyEvent.KEYCODE_VOLUME_UP:
       RtpStreamReceiver.adjust(keyCode, false);
       return true;
     case KeyEvent.KEYCODE_ENDCALL:
       if (Receiver.pstn_state == null
           || (Receiver.pstn_state.equals("IDLE")
               && (SystemClock.elapsedRealtime() - Receiver.pstn_time) > 3000)) {
         reject();
         return true;
       }
       break;
   }
   Receiver.pstn_time = 0;
   return false;
 }
  /**
   * The user successfully completed a "slide" operation. Activate whatever action the slide was
   * supposed to trigger.
   *
   * <p>(That could either be (1) hang up the ongoing call(s), or (2) answer an incoming call.)
   *
   * <p>This method is responsible for triggering any screen updates that need to happen, based on
   * any internal state changes due to the slide.
   */
  private void finishSuccessfulSlide() {
    if (DBG) log("finishSuccessfulSlide()...");

    mSlideInProgress = false;

    // TODO: Need to test lots of possible edge cases here, like if the
    // state of the Phone changes while the slide was happening.
    // (For example, say the user slid the card UP to answer an incoming
    // call, but the phone's no longer ringing by the time we got here...)

    // TODO: The state-checking logic here is very similar to the logic in
    // updateCardSlideHints().  Rather than duplicating this logic in both
    // places, maybe use a single helper function that generates a
    // complete "slidability matrix" (ie. all slide hints / states /
    // actions) based on the current state of the Phone.

    boolean phoneStateAboutToChange = false;

    // Perform the "successful slide" action.

    if (mCardAtTop) {
      // The downward slide action is to hang up any ongoing
      // call(s).
      if (DBG) log("  =========> Slide complete: HANGING UP...");
      mInCallScreen.reject();

      // Any "hangup" action is going to cause
      // the Phone state to change imminently.
      phoneStateAboutToChange = true;
    } else {
      // The upward slide action is to answer the incoming call.
      // (We put the ongoing call on hold if there's already one line in
      // use, or hang up the ongoing call if both lines are in use.)
      if (DBG) log("  =========> Slide complete: ANSWERING...");
      mInCallScreen.answer();

      // Either of the "answer call" functions is going to cause
      // the Phone state to change imminently.
      phoneStateAboutToChange = true;
    }

    // Finally, update the state of the UI depending on what just happened.
    // Update the "permanent" position of the sliding card, and the slide
    // hints.
    //
    // But *don't* do this if we know the Phone state's about to change,
    // like if the user just did a "slide up to answer".  In that case
    // we know we're going to get a onPhoneStateChanged() call in a few
    // milliseconds, and *that's* going to result in an updateScreen() call.
    // (And if we were to do that update now, we'd just get a brief flash
    // of the card at the bottom or the screen. So don't do anything
    // here.)

    if (!phoneStateAboutToChange) {
      updateCardPreferredPosition();
      updateCardSlideHints();

      // And force an immmediate re-layout.  (No need to do any
      // animation here, since the card's new "preferred position" is
      // exactly where the user just slid it.)
      mMainFrame.requestLayout();
    }
  }