2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.telecom;
19 import android.annotation.SystemApi;
20 import android.net.Uri;
21 import android.os.Bundle;
23 import java.lang.String;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
28 import java.util.Objects;
29 import java.util.concurrent.CopyOnWriteArrayList;
32 * Represents an ongoing phone call that the in-call app should present to the user.
37 public final class Call {
39 * The state of a {@code Call} when newly created.
41 public static final int STATE_NEW = 0;
44 * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
46 public static final int STATE_DIALING = 1;
49 * The state of an incoming {@code Call} when ringing locally, but not yet connected.
51 public static final int STATE_RINGING = 2;
54 * The state of a {@code Call} when in a holding state.
56 public static final int STATE_HOLDING = 3;
59 * The state of a {@code Call} when actively supporting conversation.
61 public static final int STATE_ACTIVE = 4;
64 * The state of a {@code Call} when no further voice or other communication is being
65 * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
66 * is no longer active, and the local data transport has or inevitably will release resources
67 * associated with this {@code Call}.
69 public static final int STATE_DISCONNECTED = 7;
72 * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
74 public static final int STATE_PRE_DIAL_WAIT = 8;
77 * The initial state of an outgoing {@code Call}.
78 * Common transitions are to {@link #STATE_DIALING} state for a successful call or
79 * {@link #STATE_DISCONNECTED} if it failed.
81 public static final int STATE_CONNECTING = 9;
84 * The state of a {@code Call} when the user has initiated a disconnection of the call, but the
85 * call has not yet been disconnected by the underlying {@code ConnectionService}. The next
86 * state of the call is (potentially) {@link #STATE_DISCONNECTED}.
88 public static final int STATE_DISCONNECTING = 10;
91 * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
92 * extras. Used to pass the phone accounts to display on the front end to the user in order to
93 * select phone accounts to (for example) place a call.
97 public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
99 public static class Details {
101 /** Call can currently be put on hold or unheld. */
102 public static final int CAPABILITY_HOLD = 0x00000001;
104 /** Call supports the hold feature. */
105 public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
108 * Calls within a conference can be merged. A {@link ConnectionService} has the option to
109 * add a {@link Conference} call before the child {@link Connection}s are merged. This is how
110 * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
111 * capability allows a merge button to be shown while the conference call is in the foreground
114 * This is only intended for use by a {@link Conference}.
116 public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
119 * Calls within a conference can be swapped between foreground and background.
120 * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
122 * This is only intended for use by a {@link Conference}.
124 public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
129 public static final int CAPABILITY_UNUSED = 0x00000010;
131 /** Call supports responding via text option. */
132 public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
134 /** Call can be muted. */
135 public static final int CAPABILITY_MUTE = 0x00000040;
138 * Call supports conference call management. This capability only applies to {@link Conference}
139 * calls which can have {@link Connection}s as children.
141 public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
144 * Local device supports video telephony.
147 public static final int CAPABILITY_SUPPORTS_VT_LOCAL = 0x00000100;
150 * Remote device supports video telephony.
153 public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
156 * Call is using high definition audio.
159 public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
162 * Call is using voice over WIFI.
165 public static final int CAPABILITY_VoWIFI = 0x00000800;
168 * Call is able to be separated from its parent {@code Conference}, if any.
170 public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
173 * Call is able to be individually disconnected when in a {@code Conference}.
175 public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
178 * Whether the call is a generic conference, where we do not know the precise state of
179 * participants in the conference (eg. on CDMA).
183 public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
185 private final Uri mHandle;
186 private final int mHandlePresentation;
187 private final String mCallerDisplayName;
188 private final int mCallerDisplayNamePresentation;
189 private final PhoneAccountHandle mAccountHandle;
190 private final int mCallCapabilities;
191 private final int mCallProperties;
192 private final DisconnectCause mDisconnectCause;
193 private final long mConnectTimeMillis;
194 private final GatewayInfo mGatewayInfo;
195 private final int mVideoState;
196 private final StatusHints mStatusHints;
197 private final Bundle mExtras;
200 * Whether the supplied capabilities supports the specified capability.
202 * @param capabilities A bit field of capabilities.
203 * @param capability The capability to check capabilities for.
204 * @return Whether the specified capability is supported.
207 public static boolean can(int capabilities, int capability) {
208 return (capabilities & capability) != 0;
212 * Whether the capabilities of this {@code Details} supports the specified capability.
214 * @param capability The capability to check capabilities for.
215 * @return Whether the specified capability is supported.
218 public boolean can(int capability) {
219 return can(mCallCapabilities, capability);
223 * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
225 * @param capabilities A capability bit field.
226 * @return A human readable string representation.
228 public static String capabilitiesToString(int capabilities) {
229 StringBuilder builder = new StringBuilder();
230 builder.append("[Capabilities:");
231 if (can(capabilities, CAPABILITY_HOLD)) {
232 builder.append(" CAPABILITY_HOLD");
234 if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
235 builder.append(" CAPABILITY_SUPPORT_HOLD");
237 if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
238 builder.append(" CAPABILITY_MERGE_CONFERENCE");
240 if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
241 builder.append(" CAPABILITY_SWAP_CONFERENCE");
243 if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
244 builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
246 if (can(capabilities, CAPABILITY_MUTE)) {
247 builder.append(" CAPABILITY_MUTE");
249 if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
250 builder.append(" CAPABILITY_MANAGE_CONFERENCE");
252 if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL)) {
253 builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL");
255 if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
256 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
258 if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
259 builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
261 if (can(capabilities, CAPABILITY_VoWIFI)) {
262 builder.append(" CAPABILITY_VoWIFI");
264 if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
265 builder.append(" CAPABILITY_GENERIC_CONFERENCE");
268 return builder.toString();
272 * @return The handle (e.g., phone number) to which the {@code Call} is currently
275 public Uri getHandle() {
280 * @return The presentation requirements for the handle. See
281 * {@link TelecomManager} for valid values.
283 public int getHandlePresentation() {
284 return mHandlePresentation;
288 * @return The display name for the caller.
290 public String getCallerDisplayName() {
291 return mCallerDisplayName;
295 * @return The presentation requirements for the caller display name. See
296 * {@link TelecomManager} for valid values.
298 public int getCallerDisplayNamePresentation() {
299 return mCallerDisplayNamePresentation;
303 * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
306 public PhoneAccountHandle getAccountHandle() {
307 return mAccountHandle;
311 * @return A bitmask of the capabilities of the {@code Call}, as defined by the various
312 * {@code CAPABILITY_*} constants in this class.
314 public int getCallCapabilities() {
315 return mCallCapabilities;
319 * @return A bitmask of the properties of the {@code Call}, as defined in
320 * {@link CallProperties}.
322 public int getCallProperties() {
323 return mCallProperties;
327 * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
328 * by {@link android.telecom.DisconnectCause}.
330 public DisconnectCause getDisconnectCause() {
331 return mDisconnectCause;
335 * @return The time the {@code Call} has been connected. This information is updated
336 * periodically, but user interfaces should not rely on this to display any "call time
339 public long getConnectTimeMillis() {
340 return mConnectTimeMillis;
344 * @return Information about any calling gateway the {@code Call} may be using.
346 public GatewayInfo getGatewayInfo() {
351 * @return The video state of the {@code Call}.
353 public int getVideoState() {
358 * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
361 public StatusHints getStatusHints() {
366 * @return A bundle extras to pass with the call
368 public Bundle getExtras() {
373 public boolean equals(Object o) {
374 if (o instanceof Details) {
375 Details d = (Details) o;
377 Objects.equals(mHandle, d.mHandle) &&
378 Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
379 Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
380 Objects.equals(mCallerDisplayNamePresentation,
381 d.mCallerDisplayNamePresentation) &&
382 Objects.equals(mAccountHandle, d.mAccountHandle) &&
383 Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
384 Objects.equals(mCallProperties, d.mCallProperties) &&
385 Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
386 Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
387 Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
388 Objects.equals(mVideoState, d.mVideoState) &&
389 Objects.equals(mStatusHints, d.mStatusHints) &&
390 Objects.equals(mExtras, d.mExtras);
396 public int hashCode() {
398 Objects.hashCode(mHandle) +
399 Objects.hashCode(mHandlePresentation) +
400 Objects.hashCode(mCallerDisplayName) +
401 Objects.hashCode(mCallerDisplayNamePresentation) +
402 Objects.hashCode(mAccountHandle) +
403 Objects.hashCode(mCallCapabilities) +
404 Objects.hashCode(mCallProperties) +
405 Objects.hashCode(mDisconnectCause) +
406 Objects.hashCode(mConnectTimeMillis) +
407 Objects.hashCode(mGatewayInfo) +
408 Objects.hashCode(mVideoState) +
409 Objects.hashCode(mStatusHints) +
410 Objects.hashCode(mExtras);
416 int handlePresentation,
417 String callerDisplayName,
418 int callerDisplayNamePresentation,
419 PhoneAccountHandle accountHandle,
422 DisconnectCause disconnectCause,
423 long connectTimeMillis,
424 GatewayInfo gatewayInfo,
426 StatusHints statusHints,
429 mHandlePresentation = handlePresentation;
430 mCallerDisplayName = callerDisplayName;
431 mCallerDisplayNamePresentation = callerDisplayNamePresentation;
432 mAccountHandle = accountHandle;
433 mCallCapabilities = capabilities;
434 mCallProperties = properties;
435 mDisconnectCause = disconnectCause;
436 mConnectTimeMillis = connectTimeMillis;
437 mGatewayInfo = gatewayInfo;
438 mVideoState = videoState;
439 mStatusHints = statusHints;
444 public static abstract class Listener {
446 * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
448 * @param call The {@code Call} invoking this method.
449 * @param state The new state of the {@code Call}.
451 public void onStateChanged(Call call, int state) {}
454 * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
456 * @param call The {@code Call} invoking this method.
457 * @param parent The new parent of the {@code Call}.
459 public void onParentChanged(Call call, Call parent) {}
462 * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
464 * @param call The {@code Call} invoking this method.
465 * @param children The new children of the {@code Call}.
467 public void onChildrenChanged(Call call, List<Call> children) {}
470 * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
472 * @param call The {@code Call} invoking this method.
473 * @param details A {@code Details} object describing the {@code Call}.
475 public void onDetailsChanged(Call call, Details details) {}
478 * Invoked when the text messages that can be used as responses to the incoming
479 * {@code Call} are loaded from the relevant database.
480 * See {@link #getCannedTextResponses()}.
482 * @param call The {@code Call} invoking this method.
483 * @param cannedTextResponses The text messages useable as responses.
485 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
488 * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
489 * character. This causes the post-dial signals to stop pending user confirmation. An
490 * implementation should present this choice to the user and invoke
491 * {@link #postDialContinue(boolean)} when the user makes the choice.
493 * @param call The {@code Call} invoking this method.
494 * @param remainingPostDialSequence The post-dial characters that remain to be sent.
496 public void onPostDialWait(Call call, String remainingPostDialSequence) {}
499 * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
501 * @param call The {@code Call} invoking this method.
502 * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
505 public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
508 * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
509 * up their UI for the {@code Call} in response to state transitions. Specifically,
510 * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
511 * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
512 * clients should wait for this method to be invoked.
514 * @param call The {@code Call} being destroyed.
516 public void onCallDestroyed(Call call) {}
519 * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
522 * @param call The {@code Call} being updated.
523 * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
526 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
529 private final Phone mPhone;
530 private final String mTelecomCallId;
531 private final InCallAdapter mInCallAdapter;
532 private final List<String> mChildrenIds = new ArrayList<>();
533 private final List<Call> mChildren = new ArrayList<>();
534 private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
535 private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
536 private final List<Call> mConferenceableCalls = new ArrayList<>();
537 private final List<Call> mUnmodifiableConferenceableCalls =
538 Collections.unmodifiableList(mConferenceableCalls);
540 private boolean mChildrenCached;
541 private String mParentId = null;
543 private List<String> mCannedTextResponses = null;
544 private String mRemainingPostDialSequence;
545 private InCallService.VideoCall mVideoCall;
546 private Details mDetails;
549 * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
551 * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
552 * remaining or this {@code Call} is not in a post-dial state.
554 public String getRemainingPostDialSequence() {
555 return mRemainingPostDialSequence;
559 * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
560 * @param videoState The video state in which to answer the call.
562 public void answer(int videoState) {
563 mInCallAdapter.answerCall(mTelecomCallId, videoState);
567 * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
569 * @param rejectWithMessage Whether to reject with a text message.
570 * @param textMessage An optional text message with which to respond.
572 public void reject(boolean rejectWithMessage, String textMessage) {
573 mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
577 * Instructs this {@code Call} to disconnect.
579 public void disconnect() {
580 mInCallAdapter.disconnectCall(mTelecomCallId);
584 * Instructs this {@code Call} to go on hold.
587 mInCallAdapter.holdCall(mTelecomCallId);
591 * Instructs this {@link #STATE_HOLDING} call to release from hold.
593 public void unhold() {
594 mInCallAdapter.unholdCall(mTelecomCallId);
598 * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
600 * Any other currently playing DTMF tone in the specified call is immediately stopped.
602 * @param digit A character representing the DTMF digit for which to play the tone. This
603 * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
605 public void playDtmfTone(char digit) {
606 mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
610 * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
613 * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
614 * currently playing, this method will do nothing.
616 public void stopDtmfTone() {
617 mInCallAdapter.stopDtmfTone(mTelecomCallId);
621 * Instructs this {@code Call} to continue playing a post-dial DTMF string.
623 * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
624 * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
626 * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
627 * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
629 * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
630 * {@code Call} will pause playing the tones and notify listeners via
631 * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
632 * should display to the user an indication of this state and an affordance to continue
633 * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
634 * app should invoke the {@link #postDialContinue(boolean)} method.
636 * @param proceed Whether or not to continue with the post-dial sequence.
638 public void postDialContinue(boolean proceed) {
639 mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
643 * Notifies this {@code Call} that an account has been selected and to proceed with placing
644 * an outgoing call. Optionally sets this account as the default account.
646 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
647 mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault);
652 * Instructs this {@code Call} to enter a conference.
654 * @param callToConferenceWith The other call with which to conference.
656 public void conference(Call callToConferenceWith) {
657 if (callToConferenceWith != null) {
658 mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
663 * Instructs this {@code Call} to split from any conference call with which it may be
666 public void splitFromConference() {
667 mInCallAdapter.splitFromConference(mTelecomCallId);
671 * Merges the calls within this conference. See {@link Details#CAPABILITY_MERGE_CONFERENCE}.
673 public void mergeConference() {
674 mInCallAdapter.mergeConference(mTelecomCallId);
678 * Swaps the calls within this conference. See {@link Details#CAPABILITY_SWAP_CONFERENCE}.
680 public void swapConference() {
681 mInCallAdapter.swapConference(mTelecomCallId);
685 * Obtains the parent of this {@code Call} in a conference, if any.
687 * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
688 * child of any conference {@code Call}s.
690 public Call getParent() {
691 if (mParentId != null) {
692 return mPhone.internalGetCallByTelecomId(mParentId);
698 * Obtains the children of this conference {@code Call}, if any.
700 * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
701 * {@code List} otherwise.
703 public List<Call> getChildren() {
704 if (!mChildrenCached) {
705 mChildrenCached = true;
708 for(String id : mChildrenIds) {
709 Call call = mPhone.internalGetCallByTelecomId(id);
711 // At least one child was still not found, so do not save true for "cached"
712 mChildrenCached = false;
719 return mUnmodifiableChildren;
723 * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
725 * @return The list of conferenceable {@code Call}s.
727 public List<Call> getConferenceableCalls() {
728 return mUnmodifiableConferenceableCalls;
732 * Obtains the state of this {@code Call}.
734 * @return A state value, chosen from the {@code STATE_*} constants.
736 public int getState() {
741 * Obtains a list of canned, pre-configured message responses to present to the user as
742 * ways of rejecting this {@code Call} using via a text message.
744 * @see #reject(boolean, String)
746 * @return A list of canned text message responses.
748 public List<String> getCannedTextResponses() {
749 return mCannedTextResponses;
753 * Obtains an object that can be used to display video from this {@code Call}.
755 * @return An {@code Call.VideoCall}.
758 public InCallService.VideoCall getVideoCall() {
763 * Obtains an object containing call details.
765 * @return A {@link Details} object. Depending on the state of the {@code Call}, the
766 * result may be {@code null}.
768 public Details getDetails() {
773 * Adds a listener to this {@code Call}.
775 * @param listener A {@code Listener}.
777 public void addListener(Listener listener) {
778 mListeners.add(listener);
782 * Removes a listener from this {@code Call}.
784 * @param listener A {@code Listener}.
786 public void removeListener(Listener listener) {
787 if (listener != null) {
788 mListeners.remove(listener);
793 Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
795 mTelecomCallId = telecomCallId;
796 mInCallAdapter = inCallAdapter;
801 final String internalGetCallId() {
802 return mTelecomCallId;
806 final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
807 // First, we update the internal state as far as possible before firing any updates.
808 Details details = new Details(
809 parcelableCall.getHandle(),
810 parcelableCall.getHandlePresentation(),
811 parcelableCall.getCallerDisplayName(),
812 parcelableCall.getCallerDisplayNamePresentation(),
813 parcelableCall.getAccountHandle(),
814 parcelableCall.getCapabilities(),
815 parcelableCall.getProperties(),
816 parcelableCall.getDisconnectCause(),
817 parcelableCall.getConnectTimeMillis(),
818 parcelableCall.getGatewayInfo(),
819 parcelableCall.getVideoState(),
820 parcelableCall.getStatusHints(),
821 parcelableCall.getExtras());
822 boolean detailsChanged = !Objects.equals(mDetails, details);
823 if (detailsChanged) {
827 boolean cannedTextResponsesChanged = false;
828 if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
829 && !parcelableCall.getCannedSmsResponses().isEmpty()) {
830 mCannedTextResponses =
831 Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
834 boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
835 if (videoCallChanged) {
836 mVideoCall = parcelableCall.getVideoCall();
839 int state = stateFromParcelableCallState(parcelableCall.getState());
840 boolean stateChanged = mState != state;
845 String parentId = parcelableCall.getParentCallId();
846 boolean parentChanged = !Objects.equals(mParentId, parentId);
848 mParentId = parentId;
851 List<String> childCallIds = parcelableCall.getChildCallIds();
852 boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
853 if (childrenChanged) {
854 mChildrenIds.clear();
855 mChildrenIds.addAll(parcelableCall.getChildCallIds());
856 mChildrenCached = false;
859 List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
860 List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
861 for (String otherId : conferenceableCallIds) {
862 if (callIdMap.containsKey(otherId)) {
863 conferenceableCalls.add(callIdMap.get(otherId));
867 if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
868 mConferenceableCalls.clear();
869 mConferenceableCalls.addAll(conferenceableCalls);
870 fireConferenceableCallsChanged();
873 // Now we fire updates, ensuring that any client who listens to any of these notifications
874 // gets the most up-to-date state.
877 fireStateChanged(mState);
879 if (detailsChanged) {
880 fireDetailsChanged(mDetails);
882 if (cannedTextResponsesChanged) {
883 fireCannedTextResponsesLoaded(mCannedTextResponses);
885 if (videoCallChanged) {
886 fireVideoCallChanged(mVideoCall);
889 fireParentChanged(getParent());
891 if (childrenChanged) {
892 fireChildrenChanged(getChildren());
895 // If we have transitioned to DISCONNECTED, that means we need to notify clients and
896 // remove ourselves from the Phone. Note that we do this after completing all state updates
897 // so a client can cleanly transition all their UI to the state appropriate for a
898 // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
899 if (mState == STATE_DISCONNECTED) {
901 mPhone.internalRemoveCall(this);
906 final void internalSetPostDialWait(String remaining) {
907 mRemainingPostDialSequence = remaining;
908 firePostDialWait(mRemainingPostDialSequence);
912 final void internalSetDisconnected() {
913 if (mState != Call.STATE_DISCONNECTED) {
914 mState = Call.STATE_DISCONNECTED;
915 fireStateChanged(mState);
917 mPhone.internalRemoveCall(this);
921 private void fireStateChanged(int newState) {
922 for (Listener listener : mListeners) {
923 listener.onStateChanged(this, newState);
927 private void fireParentChanged(Call newParent) {
928 for (Listener listener : mListeners) {
929 listener.onParentChanged(this, newParent);
933 private void fireChildrenChanged(List<Call> children) {
934 for (Listener listener : mListeners) {
935 listener.onChildrenChanged(this, children);
939 private void fireDetailsChanged(Details details) {
940 for (Listener listener : mListeners) {
941 listener.onDetailsChanged(this, details);
945 private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
946 for (Listener listener : mListeners) {
947 listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
951 private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
952 for (Listener listener : mListeners) {
953 listener.onVideoCallChanged(this, videoCall);
957 private void firePostDialWait(String remainingPostDialSequence) {
958 for (Listener listener : mListeners) {
959 listener.onPostDialWait(this, remainingPostDialSequence);
963 private void fireCallDestroyed() {
964 for (Listener listener : mListeners) {
965 listener.onCallDestroyed(this);
969 private void fireConferenceableCallsChanged() {
970 for (Listener listener : mListeners) {
971 listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
975 private int stateFromParcelableCallState(int parcelableCallState) {
976 switch (parcelableCallState) {
979 case CallState.CONNECTING:
980 return STATE_CONNECTING;
981 case CallState.PRE_DIAL_WAIT:
982 return STATE_PRE_DIAL_WAIT;
983 case CallState.DIALING:
984 return STATE_DIALING;
985 case CallState.RINGING:
986 return STATE_RINGING;
987 case CallState.ACTIVE:
989 case CallState.ON_HOLD:
990 return STATE_HOLDING;
991 case CallState.DISCONNECTED:
992 return STATE_DISCONNECTED;
993 case CallState.ABORTED:
994 return STATE_DISCONNECTED;
995 case CallState.DISCONNECTING:
996 return STATE_DISCONNECTING;
998 Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);