private void startUi(InCallState inCallState) {
    final Call incomingCall = mCallList.getIncomingCall();
    final boolean isCallWaiting =
        (incomingCall != null && incomingCall.getState() == Call.State.CALL_WAITING);

    // If the screen is off, we need to make sure it gets turned on for incoming calls.
    // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
    // when the activity is first created. Therefore, to ensure the screen is turned on
    // for the call waiting case, we finish() the current activity and start a new one.
    // There should be no jank from this since the screen is already off and will remain so
    // until our new activity is up.
    if (mProximitySensor.isScreenReallyOff() && isCallWaiting) {
      if (isActivityStarted()) {
        mInCallActivity.finish();
      }
      mInCallActivity = null;
    }

    final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    // If the screen is on, we'll prefer to not interrupt the user too much and slide in a card
    if (pm.isScreenOn()) {
      Intent intent = new Intent(mContext, InCallCardActivity.class);
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      mContext.startActivity(intent);
    } else {
      mStatusBarNotifier.updateNotificationAndLaunchIncomingCallUi(inCallState, mCallList);
    }
  }
  /**
   * 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 a call becomes disconnected. Called everytime an existing call changes from being
   * connected (incoming/outgoing/active) to disconnected.
   */
  @Override
  public void onDisconnect(Call call) {
    hideDialpadForDisconnect();
    maybeShowErrorDialogOnDisconnect(call);

    // We need to do the run the same code as onCallListChange.
    onCallListChange(CallList.getInstance());

    if (isActivityStarted()) {
      mInCallActivity.dismissKeyguard(false);
    }
  }
  private void attemptFinishActivity() {
    // Finish our presenter card in all cases, we won't need it anymore whatever might
    // happen.
    if (mInCallCardActivity != null) {
      mInCallCardActivity.finish();
    }

    final boolean doFinish = (mInCallActivity != null && isActivityStarted());
    Log.i(this, "Hide in call UI: " + doFinish);

    if (doFinish) {
      mInCallActivity.finish();
    }
  }
  /**
   * When the state of in-call changes, this is the first method to get called. It determines if the
   * UI needs to be started or finished depending on the new state and does it.
   */
  private InCallState startOrFinishUi(InCallState newState) {
    Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState);

    // TODO: Consider a proper state machine implementation

    // If the state isn't changing, we have already done any starting/stopping of
    // activities in a previous pass...so lets cut out early
    if (newState == mInCallState) {
      return newState;
    }

    // A new Incoming call means that the user needs to be notified of the the call (since
    // it wasn't them who initiated it).  We do this through full screen notifications and
    // happens indirectly through {@link StatusBarListener}.
    //
    // The process for incoming calls is as follows:
    //
    // 1) CallList          - Announces existence of new INCOMING call
    // 2) InCallPresenter   - Gets announcement and calculates that the new InCallState
    //                      - should be set to INCOMING.
    // 3) InCallPresenter   - This method is called to see if we need to start or finish
    //                        the app given the new state.
    // 4) StatusBarNotifier - Listens to InCallState changes. InCallPresenter calls
    //                        StatusBarNotifier explicitly to issue a FullScreen Notification
    //                        that will either start the InCallActivity or show the user a
    //                        top-level notification dialog if the user is in an immersive app.
    //                        That notification can also start the InCallActivity.
    // 5) InCallActivity    - Main activity starts up and at the end of its onCreate will
    //                        call InCallPresenter::setActivity() to let the presenter
    //                        know that start-up is complete.
    //
    //          [ AND NOW YOU'RE IN THE CALL. voila! ]
    //
    // Our app is started using a fullScreen notification.  We need to do this whenever
    // we get an incoming call.
    final boolean startStartupSequence = (InCallState.INCOMING == newState);

    // A new outgoing call indicates that the user just now dialed a number and when that
    // happens we need to display the screen immediateley.
    //
    // This is different from the incoming call sequence because we do not need to shock the
    // user with a top-level notification.  Just show the call UI normally.
    final boolean showCallUi = (InCallState.OUTGOING == newState);

    // TODO: Can we be suddenly in a call without it having been in the outgoing or incoming
    // state?  I havent seen that but if it can happen, the code below should be enabled.
    // showCallUi |= (InCallState.INCALL && !isActivityStarted());

    // The only time that we have an instance of mInCallActivity and it isn't started is
    // when it is being destroyed.  In that case, lets avoid bringing up another instance of
    // the activity.  When it is finally destroyed, we double check if we should bring it back
    // up so we aren't going to lose anything by avoiding a second startup here.
    boolean activityIsFinishing = mInCallActivity != null && !isActivityStarted();
    if (activityIsFinishing) {
      Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState);
      return mInCallState;
    }

    if (showCallUi) {
      Log.i(this, "Start in call UI");
      showInCall(false);
    } else if (startStartupSequence) {
      Log.i(this, "Start Full Screen in call UI");

      // We're about the bring up the in-call UI for an incoming call. If we still have
      // dialogs up, we need to clear them out before showing incoming screen.
      if (isActivityStarted()) {
        mInCallActivity.dismissPendingDialogs();
      }
      startUi(newState);
    } else if (newState == InCallState.NO_CALLS) {
      // The new state is the no calls state.  Tear everything down.
      attemptFinishActivity();
      attemptCleanup();
    }

    return newState;
  }
 /** Hides the dialpad. Called when a call is disconnected (Requires hiding dialpad). */
 private void hideDialpadForDisconnect() {
   if (isActivityStarted()) {
     mInCallActivity.hideDialpadForDisconnect();
   }
 }
 /**
  * For some disconnected causes, we show a dialog. This calls into the activity to show the dialog
  * if appropriate for the call.
  */
 private void maybeShowErrorDialogOnDisconnect(Call call) {
   // For newly disconnected calls, we may want to show a dialog on specific error conditions
   if (isActivityStarted() && call.getState() == Call.State.DISCONNECTED) {
     mInCallActivity.maybeShowErrorDialogOnDisconnect(call.getDisconnectCause());
   }
 }
 public void onPostDialCharWait(int callId, String chars) {
   mInCallActivity.showPostCharWaitDialog(callId, chars);
 }
 /**
  * Returns true of the activity has been created and is running. Returns true as long as activity
  * is not destroyed or finishing. This ensures that we return true even if the activity is paused
  * (not in foreground).
  */
 public boolean isActivityStarted() {
   return (mInCallActivity != null
       && !mInCallActivity.isDestroyed()
       && !mInCallActivity.isFinishing());
 }
 /** Returns true if the incall app is the foreground application. */
 public boolean isShowingInCallUi() {
   return (isActivityStarted() && mInCallActivity.isForegroundActivity());
 }