/**
   * Called when there is a change to the call list. Sets the In-Call state for the entire in-call
   * app based on the information it gets from CallList. Dispatches the in-call state to all
   * listeners. Can trigger the creation or destruction of the UI based on the states that is
   * calculates.
   */
  @Override
  public void onCallListChange(CallList callList) {
    if (callList == null) {
      return;
    }
    InCallState newState = getPotentialStateFromCallList(callList);
    newState = startOrFinishUi(newState);

    // Renable notification shade and soft navigation buttons, if we are no longer in the
    // incoming call screen
    if (!newState.isIncoming()) {
      if (mAccelerometerListener != null) {
        mAccelerometerListener.enableSensor(false);
      }
      CallCommandClient.getInstance().setSystemBarNavigationEnabled(true);
    }

    // Set the new state before announcing it to the world
    Log.i(this, "Phone switching state: " + mInCallState + " -> " + newState);
    mInCallState = newState;

    // notify listeners of new state
    for (InCallStateListener listener : mListeners) {
      Log.d(this, "Notify " + listener + " of state " + mInCallState.toString());
      listener.onStateChange(mInCallState, callList);
    }

    if (isActivityStarted()) {
      final boolean hasCall =
          callList.getActiveOrBackgroundCall() != null || callList.getOutgoingCall() != null;
      mInCallActivity.dismissKeyguard(hasCall);
    }
  }
  /**
   * Called when there is a new incoming call.
   *
   * @param call
   */
  @Override
  public void onIncomingCall(Call call) {
    InCallState newState = startOrFinishUi(InCallState.INCOMING);

    Log.i(this, "Phone switching state: " + mInCallState + " -> " + newState);
    mInCallState = newState;

    // Disable notification shade and soft navigation buttons
    if (newState.isIncoming()) {
      CallCommandClient.getInstance().setSystemBarNavigationEnabled(false);
      if (mAccelerometerListener != null) {
        mAccelerometerListener.enableSensor(true);
      }
    }

    for (IncomingCallListener listener : mIncomingCallListeners) {
      listener.onIncomingCall(mInCallState, call);
    }
  }
  /** Hangs up any active or outgoing calls. */
  public void hangUpOngoingCall(Context context) {
    // By the time we receive this intent, we could be shut down and call list
    // could be null.  Bail in those cases.
    if (mCallList == null) {
      if (mStatusBarNotifier == null) {
        // The In Call UI has crashed but the notification still stayed up. We should not
        // come to this stage.
        StatusBarNotifier.clearInCallNotification(context);
      }
      return;
    }

    Call call = mCallList.getOutgoingCall();
    if (call == null) {
      call = mCallList.getActiveOrBackgroundCall();
    }

    if (call != null) {
      CallCommandClient.getInstance().disconnectCall(call.getCallId());
    }
  }
  /**
   * Handles the green CALL key while in-call.
   *
   * @return true if we consumed the event.
   */
  public boolean handleCallKey() {
    Log.v(this, "handleCallKey");

    // The green CALL button means either "Answer", "Unhold", or
    // "Swap calls", or can be a no-op, depending on the current state
    // of the Phone.

    /** INCOMING CALL */
    final CallList calls = CallList.getInstance();
    final Call incomingCall = calls.getIncomingCall();
    Log.v(this, "incomingCall: " + incomingCall);

    // (1) Attempt to answer a call
    if (incomingCall != null) {
      CallCommandClient.getInstance().answerCall(incomingCall.getCallId());
      if (mAccelerometerListener != null) {
        mAccelerometerListener.enableSensor(false);
      }
      return true;
    }

    /** ACTIVE CALL */
    final Call activeCall = calls.getActiveCall();
    if (activeCall != null) {
      // TODO: This logic is repeated from CallButtonPresenter.java. We should
      // consolidate this logic.
      final boolean isGeneric = activeCall.can(Capabilities.GENERIC_CONFERENCE);
      final boolean canMerge = activeCall.can(Capabilities.MERGE_CALLS);
      final boolean canSwap = activeCall.can(Capabilities.SWAP_CALLS);

      Log.v(
          this,
          "activeCall: "
              + activeCall
              + ", isGeneric: "
              + isGeneric
              + ", canMerge: "
              + canMerge
              + ", canSwap: "
              + canSwap);

      // (2) Attempt actions on Generic conference calls
      if (activeCall.isConferenceCall() && isGeneric) {
        if (canMerge) {
          CallCommandClient.getInstance().merge();
          return true;
        } else if (canSwap) {
          CallCommandClient.getInstance().swap();
          return true;
        }
      }

      // (3) Swap calls
      if (canSwap) {
        CallCommandClient.getInstance().swap();
        return true;
      }
    }

    /** BACKGROUND CALL */
    final Call heldCall = calls.getBackgroundCall();
    if (heldCall != null) {
      // We have a hold call so presumeable it will always support HOLD...but
      // there is no harm in double checking.
      final boolean canHold = heldCall.can(Capabilities.HOLD);

      Log.v(this, "heldCall: " + heldCall + ", canHold: " + canHold);

      // (4) unhold call
      if (heldCall.getState() == Call.State.ONHOLD && canHold) {
        CallCommandClient.getInstance().hold(heldCall.getCallId(), false);
        return true;
      }
    }

    // Always consume hard keys
    return true;
  }