/**
  * A dialog could have prevented in-call screen from being previously finished. This function
  * checks to see if there should be any UI left and if not attempts to tear down the UI.
  */
 public void onDismissDialog() {
   Log.i(this, "Dialog dismissed");
   if (mInCallState == InCallState.NO_CALLS) {
     attemptFinishActivity();
     attemptCleanup();
   }
 }
  /**
   * 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;
  }
  /**
   * Called when the UI begins or ends. Starts the callstate callbacks if the UI just began.
   * Attempts to tear down everything if the UI just ended. See #tearDown for more insight on the
   * tear-down process.
   */
  public void setActivity(InCallActivity inCallActivity) {
    boolean updateListeners = false;
    boolean doAttemptCleanup = false;

    if (inCallActivity != null) {
      if (mInCallActivity == null) {
        updateListeners = true;
        Log.i(this, "UI Initialized");
      } else if (mInCallActivity != inCallActivity) {
        Log.wtf(this, "Setting a second activity before destroying the first.");
      } else {
        // since setActivity is called onStart(), it can be called multiple times.
        // This is fine and ignorable, but we do not want to update the world every time
        // this happens (like going to/from background) so we do not set updateListeners.
      }

      mInCallActivity = inCallActivity;

      // By the time the UI finally comes up, the call may already be disconnected.
      // If that's the case, we may need to show an error dialog.
      if (mCallList != null && mCallList.getDisconnectedCall() != null) {
        maybeShowErrorDialogOnDisconnect(mCallList.getDisconnectedCall());
      }

      // When the UI comes up, we need to first check the in-call state.
      // If we are showing NO_CALLS, that means that a call probably connected and
      // then immediately disconnected before the UI was able to come up.
      // If we dont have any calls, start tearing down the UI instead.
      // NOTE: This code relies on {@link #mInCallActivity} being set so we run it after
      // it has been set.
      if (mInCallState == InCallState.NO_CALLS) {
        Log.i(this, "UI Intialized, but no calls left.  shut down.");
        attemptFinishActivity();
        return;
      }
    } else {
      Log.i(this, "UI Destroyed)");
      updateListeners = true;
      mInCallActivity = null;

      // We attempt cleanup for the destroy case but only after we recalculate the state
      // to see if we need to come back up or stay shut down. This is why we do the cleanup
      // after the call to onCallListChange() instead of directly here.
      doAttemptCleanup = true;
    }

    // Messages can come from the telephony layer while the activity is coming up
    // and while the activity is going down.  So in both cases we need to recalculate what
    // state we should be in after they complete.
    // Examples: (1) A new incoming call could come in and then get disconnected before
    //               the activity is created.
    //           (2) All calls could disconnect and then get a new incoming call before the
    //               activity is destroyed.
    //
    // b/1122139 - We previously had a check for mServiceConnected here as well, but there are
    // cases where we need to recalculate the current state even if the service in not
    // connected.  In particular the case where startOrFinish() is called while the app is
    // already finish()ing. In that case, we skip updating the state with the knowledge that
    // we will check again once the activity has finished. That means we have to recalculate the
    // state here even if the service is disconnected since we may not have finished a state
    // transition while finish()ing.
    if (updateListeners) {
      onCallListChange(mCallList);
    }

    if (doAttemptCleanup) {
      attemptCleanup();
    }
  }
 /**
  * Called when the telephony service has disconnected from us. This will happen when there are no
  * more active calls. However, we may still want to continue showing the UI for certain cases like
  * showing "Call Ended". What we really want is to wait for the activity and the service to both
  * disconnect before we tear things down. This method sets a serviceConnected boolean and calls a
  * secondary method that performs the aforementioned logic.
  */
 public void tearDown() {
   Log.d(this, "tearDown");
   mServiceConnected = false;
   attemptCleanup();
 }