summaryrefslogtreecommitdiffstats
path: root/telecomm/java/android/telecom/Call.java
diff options
context:
space:
mode:
Diffstat (limited to 'telecomm/java/android/telecom/Call.java')
-rw-r--r--telecomm/java/android/telecom/Call.java842
1 files changed, 842 insertions, 0 deletions
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
new file mode 100644
index 0000000..1d33b3b
--- /dev/null
+++ b/telecomm/java/android/telecom/Call.java
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.DisconnectCause;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Represents an ongoing phone call that the in-call app should present to the user.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class Call {
+ /**
+ * The state of a {@code Call} when newly created.
+ */
+ public static final int STATE_NEW = 0;
+
+ /**
+ * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
+ */
+ public static final int STATE_DIALING = 1;
+
+ /**
+ * The state of an incoming {@code Call} when ringing locally, but not yet connected.
+ */
+ public static final int STATE_RINGING = 2;
+
+ /**
+ * The state of a {@code Call} when in a holding state.
+ */
+ public static final int STATE_HOLDING = 3;
+
+ /**
+ * The state of a {@code Call} when actively supporting conversation.
+ */
+ public static final int STATE_ACTIVE = 4;
+
+ /**
+ * The state of a {@code Call} when no further voice or other communication is being
+ * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
+ * is no longer active, and the local data transport has or inevitably will release resources
+ * associated with this {@code Call}.
+ */
+ public static final int STATE_DISCONNECTED = 7;
+
+ /**
+ * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+ */
+ public static final int STATE_PRE_DIAL_WAIT = 8;
+
+ /**
+ * The initial state of an outgoing {@code Call}.
+ * Common transitions are to {@link #STATE_DIALING} state for a successful call or
+ * {@link #STATE_DISCONNECTED} if it failed.
+ */
+ public static final int STATE_CONNECTING = 9;
+
+ public static class Details {
+ private final Uri mHandle;
+ private final int mHandlePresentation;
+ private final String mCallerDisplayName;
+ private final int mCallerDisplayNamePresentation;
+ private final PhoneAccountHandle mAccountHandle;
+ private final int mCallCapabilities;
+ private final int mCallProperties;
+ private final int mDisconnectCauseCode;
+ private final String mDisconnectCauseMessage;
+ private final long mConnectTimeMillis;
+ private final GatewayInfo mGatewayInfo;
+ private final int mVideoState;
+ private final StatusHints mStatusHints;
+ private final Bundle mExtras;
+
+ /**
+ * @return The handle (e.g., phone number) to which the {@code Call} is currently
+ * connected.
+ */
+ public Uri getHandle() {
+ return mHandle;
+ }
+
+ /**
+ * @return The presentation requirements for the handle. See
+ * {@link TelecomManager} for valid values.
+ */
+ public int getHandlePresentation() {
+ return mHandlePresentation;
+ }
+
+ /**
+ * @return The display name for the caller.
+ */
+ public String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ /**
+ * @return The presentation requirements for the caller display name. See
+ * {@link TelecomManager} for valid values.
+ */
+ public int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ /**
+ * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
+ * routed.
+ */
+ public PhoneAccountHandle getAccountHandle() {
+ return mAccountHandle;
+ }
+
+ /**
+ * @return A bitmask of the capabilities of the {@code Call}, as defined in
+ * {@link PhoneCapabilities}.
+ */
+ public int getCallCapabilities() {
+ return mCallCapabilities;
+ }
+
+ /**
+ * @return A bitmask of the properties of the {@code Call}, as defined in
+ * {@link CallProperties}.
+ */
+ public int getCallProperties() {
+ return mCallProperties;
+ }
+
+ /**
+ * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
+ * as a code chosen from among those declared in {@link DisconnectCause}.
+ */
+ public int getDisconnectCauseCode() {
+ return mDisconnectCauseCode;
+ }
+
+ /**
+ * @return For a {@link #STATE_DISCONNECTED} {@code Call}, an optional reason for
+ * disconnection expressed as a free text message.
+ */
+ public String getDisconnectCauseMessage() {
+ return mDisconnectCauseMessage;
+ }
+
+ /**
+ * @return The time the {@code Call} has been connected. This information is updated
+ * periodically, but user interfaces should not rely on this to display any "call time
+ * clock".
+ */
+ public long getConnectTimeMillis() {
+ return mConnectTimeMillis;
+ }
+
+ /**
+ * @return Information about any calling gateway the {@code Call} may be using.
+ */
+ public GatewayInfo getGatewayInfo() {
+ return mGatewayInfo;
+ }
+
+ /**
+ * @return The video state of the {@code Call}.
+ */
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ /**
+ * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
+ * have been set.
+ */
+ public StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ /**
+ * @return A bundle extras to pass with the call
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Details) {
+ Details d = (Details) o;
+ return
+ Objects.equals(mHandle, d.mHandle) &&
+ Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
+ Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
+ Objects.equals(mCallerDisplayNamePresentation,
+ d.mCallerDisplayNamePresentation) &&
+ Objects.equals(mAccountHandle, d.mAccountHandle) &&
+ Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
+ Objects.equals(mCallProperties, d.mCallProperties) &&
+ Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
+ Objects.equals(mDisconnectCauseMessage, d.mDisconnectCauseMessage) &&
+ Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
+ Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
+ Objects.equals(mVideoState, d.mVideoState) &&
+ Objects.equals(mStatusHints, d.mStatusHints) &&
+ Objects.equals(mExtras, d.mExtras);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return
+ Objects.hashCode(mHandle) +
+ Objects.hashCode(mHandlePresentation) +
+ Objects.hashCode(mCallerDisplayName) +
+ Objects.hashCode(mCallerDisplayNamePresentation) +
+ Objects.hashCode(mAccountHandle) +
+ Objects.hashCode(mCallCapabilities) +
+ Objects.hashCode(mCallProperties) +
+ Objects.hashCode(mDisconnectCauseCode) +
+ Objects.hashCode(mDisconnectCauseMessage) +
+ Objects.hashCode(mConnectTimeMillis) +
+ Objects.hashCode(mGatewayInfo) +
+ Objects.hashCode(mVideoState) +
+ Objects.hashCode(mStatusHints) +
+ Objects.hashCode(mExtras);
+ }
+
+ /** {@hide} */
+ public Details(
+ Uri handle,
+ int handlePresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ PhoneAccountHandle accountHandle,
+ int capabilities,
+ int properties,
+ int disconnectCauseCode,
+ String disconnectCauseMessage,
+ long connectTimeMillis,
+ GatewayInfo gatewayInfo,
+ int videoState,
+ StatusHints statusHints,
+ Bundle extras) {
+ mHandle = handle;
+ mHandlePresentation = handlePresentation;
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mAccountHandle = accountHandle;
+ mCallCapabilities = capabilities;
+ mCallProperties = properties;
+ mDisconnectCauseCode = disconnectCauseCode;
+ mDisconnectCauseMessage = disconnectCauseMessage;
+ mConnectTimeMillis = connectTimeMillis;
+ mGatewayInfo = gatewayInfo;
+ mVideoState = videoState;
+ mStatusHints = statusHints;
+ mExtras = extras;
+ }
+ }
+
+ public static abstract class Listener {
+ /**
+ * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param state The new state of the {@code Call}.
+ */
+ public void onStateChanged(Call call, int state) {}
+
+ /**
+ * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param parent The new parent of the {@code Call}.
+ */
+ public void onParentChanged(Call call, Call parent) {}
+
+ /**
+ * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param children The new children of the {@code Call}.
+ */
+ public void onChildrenChanged(Call call, List<Call> children) {}
+
+ /**
+ * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param details A {@code Details} object describing the {@code Call}.
+ */
+ public void onDetailsChanged(Call call, Details details) {}
+
+ /**
+ * Invoked when the text messages that can be used as responses to the incoming
+ * {@code Call} are loaded from the relevant database.
+ * See {@link #getCannedTextResponses()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param cannedTextResponses The text messages useable as responses.
+ */
+ public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
+
+ /**
+ * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
+ * character. This causes the post-dial signals to stop pending user confirmation. An
+ * implementation should present this choice to the user and invoke
+ * {@link #postDialContinue(boolean)} when the user makes the choice.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param remainingPostDialSequence The post-dial characters that remain to be sent.
+ */
+ public void onPostDialWait(Call call, String remainingPostDialSequence) {}
+
+ /**
+ * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
+ * @hide
+ */
+ public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
+
+ /**
+ * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
+ * up their UI for the {@code Call} in response to state transitions. Specifically,
+ * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
+ * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
+ * clients should wait for this method to be invoked.
+ *
+ * @param call The {@code Call} being destroyed.
+ */
+ public void onCallDestroyed(Call call) {}
+
+ /**
+ * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
+ * conferenced.
+ *
+ * @param call The {@code Call} being updated.
+ * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
+ * conferenced.
+ */
+ public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
+ }
+
+ private final Phone mPhone;
+ private final String mTelecomCallId;
+ private final InCallAdapter mInCallAdapter;
+ private final List<String> mChildrenIds = new ArrayList<>();
+ private final List<Call> mChildren = new ArrayList<>();
+ private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
+ private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
+ private final List<Call> mConferenceableCalls = new ArrayList<>();
+ private final List<Call> mUnmodifiableConferenceableCalls =
+ Collections.unmodifiableList(mConferenceableCalls);
+
+ private boolean mChildrenCached;
+ private String mParentId = null;
+ private int mState;
+ private List<String> mCannedTextResponses = null;
+ private String mRemainingPostDialSequence;
+ private InCallService.VideoCall mVideoCall;
+ private Details mDetails;
+
+ /**
+ * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
+ *
+ * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
+ * remaining or this {@code Call} is not in a post-dial state.
+ */
+ public String getRemainingPostDialSequence() {
+ return mRemainingPostDialSequence;
+ }
+
+ /**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
+ * @param videoState The video state in which to answer the call.
+ */
+ public void answer(int videoState) {
+ mInCallAdapter.answerCall(mTelecomCallId, videoState);
+ }
+
+ /**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
+ *
+ * @param rejectWithMessage Whether to reject with a text message.
+ * @param textMessage An optional text message with which to respond.
+ */
+ public void reject(boolean rejectWithMessage, String textMessage) {
+ mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
+ }
+
+ /**
+ * Instructs this {@code Call} to disconnect.
+ */
+ public void disconnect() {
+ mInCallAdapter.disconnectCall(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to go on hold.
+ */
+ public void hold() {
+ mInCallAdapter.holdCall(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@link #STATE_HOLDING} call to release from hold.
+ */
+ public void unhold() {
+ mInCallAdapter.unholdCall(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
+ *
+ * Any other currently playing DTMF tone in the specified call is immediately stopped.
+ *
+ * @param digit A character representing the DTMF digit for which to play the tone. This
+ * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
+ */
+ public void playDtmfTone(char digit) {
+ mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
+ }
+
+ /**
+ * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
+ * currently playing.
+ *
+ * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
+ * currently playing, this method will do nothing.
+ */
+ public void stopDtmfTone() {
+ mInCallAdapter.stopDtmfTone(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to continue playing a post-dial DTMF string.
+ *
+ * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
+ * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
+ * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
+ * {@code Call} will pause playing the tones and notify listeners via
+ * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
+ * should display to the user an indication of this state and an affordance to continue
+ * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
+ * app should invoke the {@link #postDialContinue(boolean)} method.
+ *
+ * @param proceed Whether or not to continue with the post-dial sequence.
+ */
+ public void postDialContinue(boolean proceed) {
+ mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
+ }
+
+ /**
+ * Notifies this {@code Call} that an account has been selected and to proceed with placing
+ * an outgoing call.
+ */
+ public void phoneAccountSelected(PhoneAccountHandle accountHandle) {
+ mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle);
+
+ }
+
+ /**
+ * Instructs this {@code Call} to enter a conference.
+ *
+ * @param callToConferenceWith The other call with which to conference.
+ */
+ public void conference(Call callToConferenceWith) {
+ if (callToConferenceWith != null) {
+ mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
+ }
+ }
+
+ /**
+ * Instructs this {@code Call} to split from any conference call with which it may be
+ * connected.
+ */
+ public void splitFromConference() {
+ mInCallAdapter.splitFromConference(mTelecomCallId);
+ }
+
+ /**
+ * Merges the calls within this conference. See {@link PhoneCapabilities#MERGE_CONFERENCE}.
+ */
+ public void mergeConference() {
+ mInCallAdapter.mergeConference(mTelecomCallId);
+ }
+
+ /**
+ * Swaps the calls within this conference. See {@link PhoneCapabilities#SWAP_CONFERENCE}.
+ */
+ public void swapConference() {
+ mInCallAdapter.swapConference(mTelecomCallId);
+ }
+
+ /**
+ * Obtains the parent of this {@code Call} in a conference, if any.
+ *
+ * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
+ * child of any conference {@code Call}s.
+ */
+ public Call getParent() {
+ if (mParentId != null) {
+ return mPhone.internalGetCallByTelecomId(mParentId);
+ }
+ return null;
+ }
+
+ /**
+ * Obtains the children of this conference {@code Call}, if any.
+ *
+ * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
+ * {@code List} otherwise.
+ */
+ public List<Call> getChildren() {
+ if (!mChildrenCached) {
+ mChildrenCached = true;
+ mChildren.clear();
+
+ for(String id : mChildrenIds) {
+ Call call = mPhone.internalGetCallByTelecomId(id);
+ if (call == null) {
+ // At least one child was still not found, so do not save true for "cached"
+ mChildrenCached = false;
+ } else {
+ mChildren.add(call);
+ }
+ }
+ }
+
+ return mUnmodifiableChildren;
+ }
+
+ /**
+ * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
+ *
+ * @return The list of conferenceable {@code Call}s.
+ */
+ public List<Call> getConferenceableCalls() {
+ return mUnmodifiableConferenceableCalls;
+ }
+
+ /**
+ * Obtains the state of this {@code Call}.
+ *
+ * @return A state value, chosen from the {@code STATE_*} constants.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Obtains a list of canned, pre-configured message responses to present to the user as
+ * ways of rejecting this {@code Call} using via a text message.
+ *
+ * @see #reject(boolean, String)
+ *
+ * @return A list of canned text message responses.
+ */
+ public List<String> getCannedTextResponses() {
+ return mCannedTextResponses;
+ }
+
+ /**
+ * Obtains an object that can be used to display video from this {@code Call}.
+ *
+ * @return An {@code Call.VideoCall}.
+ * @hide
+ */
+ public InCallService.VideoCall getVideoCall() {
+ return mVideoCall;
+ }
+
+ /**
+ * Obtains an object containing call details.
+ *
+ * @return A {@link Details} object. Depending on the state of the {@code Call}, the
+ * result may be {@code null}.
+ */
+ public Details getDetails() {
+ return mDetails;
+ }
+
+ /**
+ * Adds a listener to this {@code Call}.
+ *
+ * @param listener A {@code Listener}.
+ */
+ public void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from this {@code Call}.
+ *
+ * @param listener A {@code Listener}.
+ */
+ public void removeListener(Listener listener) {
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
+ }
+
+ /** {@hide} */
+ Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
+ mPhone = phone;
+ mTelecomCallId = telecomCallId;
+ mInCallAdapter = inCallAdapter;
+ mState = STATE_NEW;
+ }
+
+ /** {@hide} */
+ final String internalGetCallId() {
+ return mTelecomCallId;
+ }
+
+ /** {@hide} */
+ final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
+ // First, we update the internal state as far as possible before firing any updates.
+ Details details = new Details(
+ parcelableCall.getHandle(),
+ parcelableCall.getHandlePresentation(),
+ parcelableCall.getCallerDisplayName(),
+ parcelableCall.getCallerDisplayNamePresentation(),
+ parcelableCall.getAccountHandle(),
+ parcelableCall.getCapabilities(),
+ parcelableCall.getProperties(),
+ parcelableCall.getDisconnectCauseCode(),
+ parcelableCall.getDisconnectCauseMsg(),
+ parcelableCall.getConnectTimeMillis(),
+ parcelableCall.getGatewayInfo(),
+ parcelableCall.getVideoState(),
+ parcelableCall.getStatusHints(),
+ parcelableCall.getExtras());
+ boolean detailsChanged = !Objects.equals(mDetails, details);
+ if (detailsChanged) {
+ mDetails = details;
+ }
+
+ boolean cannedTextResponsesChanged = false;
+ if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
+ && !parcelableCall.getCannedSmsResponses().isEmpty()) {
+ mCannedTextResponses =
+ Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
+ }
+
+ boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
+ if (videoCallChanged) {
+ mVideoCall = parcelableCall.getVideoCall();
+ }
+
+ int state = stateFromParcelableCallState(parcelableCall.getState());
+ boolean stateChanged = mState != state;
+ if (stateChanged) {
+ mState = state;
+ }
+
+ String parentId = parcelableCall.getParentCallId();
+ boolean parentChanged = !Objects.equals(mParentId, parentId);
+ if (parentChanged) {
+ mParentId = parentId;
+ }
+
+ List<String> childCallIds = parcelableCall.getChildCallIds();
+ boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
+ if (childrenChanged) {
+ mChildrenIds.clear();
+ mChildrenIds.addAll(parcelableCall.getChildCallIds());
+ mChildrenCached = false;
+ }
+
+ List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
+ List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
+ for (String otherId : conferenceableCallIds) {
+ if (callIdMap.containsKey(otherId)) {
+ conferenceableCalls.add(callIdMap.get(otherId));
+ }
+ }
+
+ if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
+ mConferenceableCalls.clear();
+ mConferenceableCalls.addAll(conferenceableCalls);
+ fireConferenceableCallsChanged();
+ }
+
+ // Now we fire updates, ensuring that any client who listens to any of these notifications
+ // gets the most up-to-date state.
+
+ if (stateChanged) {
+ fireStateChanged(mState);
+ }
+ if (detailsChanged) {
+ fireDetailsChanged(mDetails);
+ }
+ if (cannedTextResponsesChanged) {
+ fireCannedTextResponsesLoaded(mCannedTextResponses);
+ }
+ if (videoCallChanged) {
+ fireVideoCallChanged(mVideoCall);
+ }
+ if (parentChanged) {
+ fireParentChanged(getParent());
+ }
+ if (childrenChanged) {
+ fireChildrenChanged(getChildren());
+ }
+
+ // If we have transitioned to DISCONNECTED, that means we need to notify clients and
+ // remove ourselves from the Phone. Note that we do this after completing all state updates
+ // so a client can cleanly transition all their UI to the state appropriate for a
+ // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
+ if (mState == STATE_DISCONNECTED) {
+ fireCallDestroyed();
+ mPhone.internalRemoveCall(this);
+ }
+ }
+
+ /** {@hide} */
+ final void internalSetPostDialWait(String remaining) {
+ mRemainingPostDialSequence = remaining;
+ firePostDialWait(mRemainingPostDialSequence);
+ }
+
+ /** {@hide} */
+ final void internalSetDisconnected() {
+ if (mState != Call.STATE_DISCONNECTED) {
+ mState = Call.STATE_DISCONNECTED;
+ fireStateChanged(mState);
+ fireCallDestroyed();
+ mPhone.internalRemoveCall(this);
+ }
+ }
+
+ private void fireStateChanged(int newState) {
+ for (Listener listener : mListeners) {
+ listener.onStateChanged(this, newState);
+ }
+ }
+
+ private void fireParentChanged(Call newParent) {
+ for (Listener listener : mListeners) {
+ listener.onParentChanged(this, newParent);
+ }
+ }
+
+ private void fireChildrenChanged(List<Call> children) {
+ for (Listener listener : mListeners) {
+ listener.onChildrenChanged(this, children);
+ }
+ }
+
+ private void fireDetailsChanged(Details details) {
+ for (Listener listener : mListeners) {
+ listener.onDetailsChanged(this, details);
+ }
+ }
+
+ private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
+ for (Listener listener : mListeners) {
+ listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
+ }
+ }
+
+ private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
+ for (Listener listener : mListeners) {
+ listener.onVideoCallChanged(this, videoCall);
+ }
+ }
+
+ private void firePostDialWait(String remainingPostDialSequence) {
+ for (Listener listener : mListeners) {
+ listener.onPostDialWait(this, remainingPostDialSequence);
+ }
+ }
+
+ private void fireCallDestroyed() {
+ for (Listener listener : mListeners) {
+ listener.onCallDestroyed(this);
+ }
+ }
+
+ private void fireConferenceableCallsChanged() {
+ for (Listener listener : mListeners) {
+ listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
+ }
+ }
+
+ private int stateFromParcelableCallState(int parcelableCallState) {
+ switch (parcelableCallState) {
+ case CallState.NEW:
+ return STATE_NEW;
+ case CallState.CONNECTING:
+ return STATE_CONNECTING;
+ case CallState.PRE_DIAL_WAIT:
+ return STATE_PRE_DIAL_WAIT;
+ case CallState.DIALING:
+ return STATE_DIALING;
+ case CallState.RINGING:
+ return STATE_RINGING;
+ case CallState.ACTIVE:
+ return STATE_ACTIVE;
+ case CallState.ON_HOLD:
+ return STATE_HOLDING;
+ case CallState.DISCONNECTED:
+ return STATE_DISCONNECTED;
+ case CallState.ABORTED:
+ return STATE_DISCONNECTED;
+ default:
+ Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
+ return STATE_NEW;
+ }
+ }
+}