@Override
  public void onBackPressed() {
    Log.i(this, "onBackPressed");

    // BACK is also used to exit out of any "special modes" of the
    // in-call UI:

    if (!mConferenceManagerFragment.isVisible() && !mCallCardFragment.isVisible()) {
      return;
    }

    if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
      mCallButtonFragment.displayDialpad(false /* show */, true /* animate */);
      return;
    } else if (mConferenceManagerFragment.isVisible()) {
      showConferenceCallManager(false);
      return;
    }

    // Always disable the Back key while an incoming call is ringing
    final Call call = CallList.getInstance().getIncomingCall();
    if (call != null) {
      Log.d(this, "Consume Back press for an incoming call");
      return;
    }

    // Nothing special to do.  Fall back to the default behavior.
    super.onBackPressed();
  }
  /**
   * 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);
    }
  }
Exemplo n.º 3
0
  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    final CallList calls = CallList.getInstance();
    final Call call = calls.getFirstCall();
    getPresenter().init(getActivity(), call);
  }
  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);
    }
  }
Exemplo n.º 5
0
  @Override
  public int hashCode() {
    int result = lastDateUpdate != null ? lastDateUpdate.hashCode() : 0;
    result = 31 * result + (callList != null ? callList.hashCode() : 0);
    result = 31 * result + (status != null ? status.hashCode() : 0);
    result = 31 * result + (user != null ? user.hashCode() : 0);
    result = 31 * result + (company != null ? company.hashCode() : 0);
    result = 31 * result + (event != null ? event.hashCode() : 0);

    return result;
  }
  /** 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());
    }
  }
  /**
   * 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 relaunchedFromDialer(boolean showDialpad) {
    mShowDialpadRequested = showDialpad;
    mAnimateDialpadOnShow = true;

    if (mShowDialpadRequested) {
      // If there's only one line in use, AND it's on hold, then we're sure the user
      // wants to use the dialpad toward the exact line, so un-hold the holding line.
      final Call call = CallList.getInstance().getActiveOrBackgroundCall();
      if (call != null && call.getState() == State.ONHOLD) {
        TelecomAdapter.getInstance().unholdCall(call.getId());
      }
    }
  }
Exemplo n.º 9
0
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof CallListCompany)) return false;

    CallListCompany that = (CallListCompany) o;

    if (callList != null ? !callList.equals(that.callList) : that.callList != null) return false;
    if (company != null ? !company.equals(that.company) : that.company != null) return false;
    if (event != null ? !event.equals(that.event) : that.event != null) return false;
    if (lastDateUpdate != null
        ? !lastDateUpdate.equals(that.lastDateUpdate)
        : that.lastDateUpdate != null) return false;
    if (status != null ? !status.equals(that.status) : that.status != null) return false;
    if (user != null ? !user.equals(that.user) : that.user != null) return false;

    return true;
  }
  /**
   * Checks to see if both the UI is gone and the service is disconnected. If so, tear it all down.
   */
  private void attemptCleanup() {
    boolean shouldCleanup =
        (mInCallActivity == null && !mServiceConnected && mInCallState == InCallState.NO_CALLS);
    Log.i(this, "attemptCleanup? " + shouldCleanup);

    if (shouldCleanup) {
      mIsActivityPreviouslyStarted = false;

      // blow away stale contact info so that we get fresh data on
      // the next set of calls
      if (mContactInfoCache != null) {
        mContactInfoCache.clearCache();
      }
      mContactInfoCache = null;

      if (mProximitySensor != null) {
        removeListener(mProximitySensor);
        mProximitySensor.tearDown();
      }
      mProximitySensor = null;

      mAccelerometerListener = null;

      mAudioModeProvider = null;

      if (mStatusBarNotifier != null) {
        removeListener(mStatusBarNotifier);
      }
      mStatusBarNotifier = null;

      if (mCallList != null) {
        mCallList.removeListener(this);
      }
      mCallList = null;

      mContext = null;
      mInCallActivity = null;

      mListeners.clear();
      mIncomingCallListeners.clear();

      Log.d(this, "Finished InCallPresenter.CleanUp");
    }
  }
  public void setUp(Context context, CallList callList, AudioModeProvider audioModeProvider) {
    if (mServiceConnected) {
      Log.i(this, "New service connection replacing existing one.");
      // retain the current resources, no need to create new ones.
      Preconditions.checkState(context == mContext);
      Preconditions.checkState(callList == mCallList);
      Preconditions.checkState(audioModeProvider == mAudioModeProvider);
      return;
    }

    Preconditions.checkNotNull(context);
    mContext = context;

    mContactInfoCache = ContactInfoCache.getInstance(context);

    mStatusBarNotifier = new StatusBarNotifier(context, mContactInfoCache);
    addListener(mStatusBarNotifier);

    mAudioModeProvider = audioModeProvider;

    mProximitySensor = new ProximitySensor(context, mAudioModeProvider);
    addListener(mProximitySensor);

    mAccelerometerListener = new AccelerometerListener(context);

    mCallList = callList;

    // This only gets called by the service so this is okay.
    mServiceConnected = true;

    // The final thing we do in this set up is add ourselves as a listener to CallList.  This
    // will kick off an update and the whole process can start.
    mCallList.addListener(this);

    Log.d(this, "Finished InCallPresenter.setUp");
  }
  /** Given the call list, return the state in which the in-call screen should be. */
  public static InCallState getPotentialStateFromCallList(CallList callList) {

    InCallState newState = InCallState.NO_CALLS;

    if (callList == null) {
      return newState;
    }
    if (callList.getIncomingCall() != null) {
      newState = InCallState.INCOMING;
    } else if (callList.getOutgoingCall() != null) {
      newState = InCallState.OUTGOING;
    } else if (callList.getActiveCall() != null
        || callList.getBackgroundCall() != null
        || callList.getDisconnectedCall() != null
        || callList.getDisconnectingCall() != null) {
      newState = InCallState.INCALL;
    }

    return newState;
  }
  private void internalResolveIntent(Intent intent) {
    final String action = intent.getAction();

    if (action.equals(intent.ACTION_MAIN)) {
      // This action is the normal way to bring up the in-call UI.
      //
      // But we do check here for one extra that can come along with the
      // ACTION_MAIN intent:

      if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {
        // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
        // dialpad should be initially visible.  If the extra isn't
        // present at all, we just leave the dialpad in its previous state.

        final boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
        Log.d(this, "- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);

        relaunchedFromDialer(showDialpad);
      }

      if (intent.getBooleanExtra(NEW_OUTGOING_CALL_EXTRA, false)) {
        intent.removeExtra(NEW_OUTGOING_CALL_EXTRA);
        Call call = CallList.getInstance().getOutgoingCall();
        if (call == null) {
          call = CallList.getInstance().getPendingOutgoingCall();
        }

        Bundle extras = null;
        if (call != null) {
          extras = call.getTelecommCall().getDetails().getExtras();
        }
        if (extras == null) {
          // Initialize the extras bundle to avoid NPE
          extras = new Bundle();
        }

        Point touchPoint = null;
        if (TouchPointManager.getInstance().hasValidPoint()) {
          // Use the most immediate touch point in the InCallUi if available
          touchPoint = TouchPointManager.getInstance().getPoint();
        } else {
          // Otherwise retrieve the touch point from the call intent
          if (call != null) {
            touchPoint = (Point) extras.getParcelable(TouchPointManager.TOUCH_POINT);
          }
        }

        // This is only true in the case where an outgoing call is initiated by tapping
        // on the "Select account dialog", in which case we skip the initial animation. In
        // most other cases the circular reveal is done by OutgoingCallAnimationActivity.
        final boolean showCircularReveal =
            intent.getBooleanExtra(SHOW_CIRCULAR_REVEAL_EXTRA, false);
        mCallCardFragment.animateForNewOutgoingCall(touchPoint, showCircularReveal);

        // InCallActivity is responsible for disconnecting a new outgoing call if there
        // is no way of making it (i.e. no valid call capable accounts)
        if (InCallPresenter.isCallWithNoValidAccounts(call)) {
          TelecomAdapter.getInstance().disconnectCall(call.getId());
        }

        dismissKeyguard(true);
      }

      Call pendingAccountSelectionCall = CallList.getInstance().getWaitingForAccountCall();
      if (pendingAccountSelectionCall != null) {
        mCallCardFragment.setVisible(false);
        Bundle extras = pendingAccountSelectionCall.getTelecommCall().getDetails().getExtras();

        final List<PhoneAccountHandle> phoneAccountHandles;
        if (extras != null) {
          phoneAccountHandles =
              extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
        } else {
          phoneAccountHandles = new ArrayList<>();
        }

        SelectPhoneAccountListener listener =
            new SelectPhoneAccountListener() {
              @Override
              public void onPhoneAccountSelected(
                  PhoneAccountHandle selectedAccountHandle, boolean setDefault) {
                InCallPresenter.getInstance()
                    .handleAccountSelection(selectedAccountHandle, setDefault);
              }

              @Override
              public void onDialogDismissed() {
                InCallPresenter.getInstance().cancelAccountSelection();
              }
            };

        SelectPhoneAccountDialogFragment.showAccountDialog(
            getFragmentManager(),
            R.string.select_phone_account_for_calls,
            true,
            phoneAccountHandles,
            listener);
      } else {
        mCallCardFragment.setVisible(true);
      }

      return;
    }
  }
  /**
   * 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;
  }
  /**
   * 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();
    }
  }