Call.java revision 36c62f3e3cc155e950de8b1a1f1109d38fb32d61
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17package android.telecom; 18 19import android.annotation.SystemApi; 20import android.net.Uri; 21import android.os.Bundle; 22 23import java.lang.String; 24import java.util.ArrayList; 25import java.util.Collections; 26import java.util.List; 27import java.util.Map; 28import java.util.Objects; 29import java.util.concurrent.CopyOnWriteArrayList; 30 31/** 32 * Represents an ongoing phone call that the in-call app should present to the user. 33 * 34 * {@hide} 35 */ 36@SystemApi 37public final class Call { 38 /** 39 * The state of a {@code Call} when newly created. 40 */ 41 public static final int STATE_NEW = 0; 42 43 /** 44 * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected. 45 */ 46 public static final int STATE_DIALING = 1; 47 48 /** 49 * The state of an incoming {@code Call} when ringing locally, but not yet connected. 50 */ 51 public static final int STATE_RINGING = 2; 52 53 /** 54 * The state of a {@code Call} when in a holding state. 55 */ 56 public static final int STATE_HOLDING = 3; 57 58 /** 59 * The state of a {@code Call} when actively supporting conversation. 60 */ 61 public static final int STATE_ACTIVE = 4; 62 63 /** 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}. 68 */ 69 public static final int STATE_DISCONNECTED = 7; 70 71 /** 72 * The state of an outgoing {@code Call}, but waiting for user input before proceeding. 73 */ 74 public static final int STATE_PRE_DIAL_WAIT = 8; 75 76 /** 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. 80 */ 81 public static final int STATE_CONNECTING = 9; 82 83 /** 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}. 87 */ 88 public static final int STATE_DISCONNECTING = 10; 89 90 /** 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. 94 * 95 * @hide 96 */ 97 public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; 98 99 public static class Details { 100 private final Uri mHandle; 101 private final int mHandlePresentation; 102 private final String mCallerDisplayName; 103 private final int mCallerDisplayNamePresentation; 104 private final PhoneAccountHandle mAccountHandle; 105 private final int mCallCapabilities; 106 private final int mCallProperties; 107 private final DisconnectCause mDisconnectCause; 108 private final long mConnectTimeMillis; 109 private final GatewayInfo mGatewayInfo; 110 private final int mVideoState; 111 private final StatusHints mStatusHints; 112 private final Bundle mExtras; 113 114 /** 115 * @return The handle (e.g., phone number) to which the {@code Call} is currently 116 * connected. 117 */ 118 public Uri getHandle() { 119 return mHandle; 120 } 121 122 /** 123 * @return The presentation requirements for the handle. See 124 * {@link TelecomManager} for valid values. 125 */ 126 public int getHandlePresentation() { 127 return mHandlePresentation; 128 } 129 130 /** 131 * @return The display name for the caller. 132 */ 133 public String getCallerDisplayName() { 134 return mCallerDisplayName; 135 } 136 137 /** 138 * @return The presentation requirements for the caller display name. See 139 * {@link TelecomManager} for valid values. 140 */ 141 public int getCallerDisplayNamePresentation() { 142 return mCallerDisplayNamePresentation; 143 } 144 145 /** 146 * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being 147 * routed. 148 */ 149 public PhoneAccountHandle getAccountHandle() { 150 return mAccountHandle; 151 } 152 153 /** 154 * @return A bitmask of the capabilities of the {@code Call}, as defined in 155 * {@link PhoneCapabilities}. 156 */ 157 public int getCallCapabilities() { 158 return mCallCapabilities; 159 } 160 161 /** 162 * @return A bitmask of the properties of the {@code Call}, as defined in 163 * {@link CallProperties}. 164 */ 165 public int getCallProperties() { 166 return mCallProperties; 167 } 168 169 /** 170 * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed 171 * by {@link android.telecom.DisconnectCause}. 172 */ 173 public DisconnectCause getDisconnectCause() { 174 return mDisconnectCause; 175 } 176 177 /** 178 * @return The time the {@code Call} has been connected. This information is updated 179 * periodically, but user interfaces should not rely on this to display any "call time 180 * clock". 181 */ 182 public long getConnectTimeMillis() { 183 return mConnectTimeMillis; 184 } 185 186 /** 187 * @return Information about any calling gateway the {@code Call} may be using. 188 */ 189 public GatewayInfo getGatewayInfo() { 190 return mGatewayInfo; 191 } 192 193 /** 194 * @return The video state of the {@code Call}. 195 */ 196 public int getVideoState() { 197 return mVideoState; 198 } 199 200 /** 201 * @return The current {@link android.telecom.StatusHints}, or {@code null} if none 202 * have been set. 203 */ 204 public StatusHints getStatusHints() { 205 return mStatusHints; 206 } 207 208 /** 209 * @return A bundle extras to pass with the call 210 */ 211 public Bundle getExtras() { 212 return mExtras; 213 } 214 215 @Override 216 public boolean equals(Object o) { 217 if (o instanceof Details) { 218 Details d = (Details) o; 219 return 220 Objects.equals(mHandle, d.mHandle) && 221 Objects.equals(mHandlePresentation, d.mHandlePresentation) && 222 Objects.equals(mCallerDisplayName, d.mCallerDisplayName) && 223 Objects.equals(mCallerDisplayNamePresentation, 224 d.mCallerDisplayNamePresentation) && 225 Objects.equals(mAccountHandle, d.mAccountHandle) && 226 Objects.equals(mCallCapabilities, d.mCallCapabilities) && 227 Objects.equals(mCallProperties, d.mCallProperties) && 228 Objects.equals(mDisconnectCause, d.mDisconnectCause) && 229 Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) && 230 Objects.equals(mGatewayInfo, d.mGatewayInfo) && 231 Objects.equals(mVideoState, d.mVideoState) && 232 Objects.equals(mStatusHints, d.mStatusHints) && 233 Objects.equals(mExtras, d.mExtras); 234 } 235 return false; 236 } 237 238 @Override 239 public int hashCode() { 240 return 241 Objects.hashCode(mHandle) + 242 Objects.hashCode(mHandlePresentation) + 243 Objects.hashCode(mCallerDisplayName) + 244 Objects.hashCode(mCallerDisplayNamePresentation) + 245 Objects.hashCode(mAccountHandle) + 246 Objects.hashCode(mCallCapabilities) + 247 Objects.hashCode(mCallProperties) + 248 Objects.hashCode(mDisconnectCause) + 249 Objects.hashCode(mConnectTimeMillis) + 250 Objects.hashCode(mGatewayInfo) + 251 Objects.hashCode(mVideoState) + 252 Objects.hashCode(mStatusHints) + 253 Objects.hashCode(mExtras); 254 } 255 256 /** {@hide} */ 257 public Details( 258 Uri handle, 259 int handlePresentation, 260 String callerDisplayName, 261 int callerDisplayNamePresentation, 262 PhoneAccountHandle accountHandle, 263 int capabilities, 264 int properties, 265 DisconnectCause disconnectCause, 266 long connectTimeMillis, 267 GatewayInfo gatewayInfo, 268 int videoState, 269 StatusHints statusHints, 270 Bundle extras) { 271 mHandle = handle; 272 mHandlePresentation = handlePresentation; 273 mCallerDisplayName = callerDisplayName; 274 mCallerDisplayNamePresentation = callerDisplayNamePresentation; 275 mAccountHandle = accountHandle; 276 mCallCapabilities = capabilities; 277 mCallProperties = properties; 278 mDisconnectCause = disconnectCause; 279 mConnectTimeMillis = connectTimeMillis; 280 mGatewayInfo = gatewayInfo; 281 mVideoState = videoState; 282 mStatusHints = statusHints; 283 mExtras = extras; 284 } 285 } 286 287 public static abstract class Listener { 288 /** 289 * Invoked when the state of this {@code Call} has changed. See {@link #getState()}. 290 * 291 * @param call The {@code Call} invoking this method. 292 * @param state The new state of the {@code Call}. 293 */ 294 public void onStateChanged(Call call, int state) {} 295 296 /** 297 * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}. 298 * 299 * @param call The {@code Call} invoking this method. 300 * @param parent The new parent of the {@code Call}. 301 */ 302 public void onParentChanged(Call call, Call parent) {} 303 304 /** 305 * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}. 306 * 307 * @param call The {@code Call} invoking this method. 308 * @param children The new children of the {@code Call}. 309 */ 310 public void onChildrenChanged(Call call, List<Call> children) {} 311 312 /** 313 * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}. 314 * 315 * @param call The {@code Call} invoking this method. 316 * @param details A {@code Details} object describing the {@code Call}. 317 */ 318 public void onDetailsChanged(Call call, Details details) {} 319 320 /** 321 * Invoked when the text messages that can be used as responses to the incoming 322 * {@code Call} are loaded from the relevant database. 323 * See {@link #getCannedTextResponses()}. 324 * 325 * @param call The {@code Call} invoking this method. 326 * @param cannedTextResponses The text messages useable as responses. 327 */ 328 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {} 329 330 /** 331 * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause 332 * character. This causes the post-dial signals to stop pending user confirmation. An 333 * implementation should present this choice to the user and invoke 334 * {@link #postDialContinue(boolean)} when the user makes the choice. 335 * 336 * @param call The {@code Call} invoking this method. 337 * @param remainingPostDialSequence The post-dial characters that remain to be sent. 338 */ 339 public void onPostDialWait(Call call, String remainingPostDialSequence) {} 340 341 /** 342 * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed. 343 * 344 * @param call The {@code Call} invoking this method. 345 * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}. 346 * @hide 347 */ 348 public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {} 349 350 /** 351 * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning 352 * up their UI for the {@code Call} in response to state transitions. Specifically, 353 * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of 354 * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather, 355 * clients should wait for this method to be invoked. 356 * 357 * @param call The {@code Call} being destroyed. 358 */ 359 public void onCallDestroyed(Call call) {} 360 361 /** 362 * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be 363 * conferenced. 364 * 365 * @param call The {@code Call} being updated. 366 * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be 367 * conferenced. 368 */ 369 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {} 370 } 371 372 private final Phone mPhone; 373 private final String mTelecomCallId; 374 private final InCallAdapter mInCallAdapter; 375 private final List<String> mChildrenIds = new ArrayList<>(); 376 private final List<Call> mChildren = new ArrayList<>(); 377 private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren); 378 private final List<Listener> mListeners = new CopyOnWriteArrayList<>(); 379 private final List<Call> mConferenceableCalls = new ArrayList<>(); 380 private final List<Call> mUnmodifiableConferenceableCalls = 381 Collections.unmodifiableList(mConferenceableCalls); 382 383 private boolean mChildrenCached; 384 private String mParentId = null; 385 private int mState; 386 private List<String> mCannedTextResponses = null; 387 private String mRemainingPostDialSequence; 388 private InCallService.VideoCall mVideoCall; 389 private Details mDetails; 390 391 /** 392 * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any. 393 * 394 * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence 395 * remaining or this {@code Call} is not in a post-dial state. 396 */ 397 public String getRemainingPostDialSequence() { 398 return mRemainingPostDialSequence; 399 } 400 401 /** 402 * Instructs this {@link #STATE_RINGING} {@code Call} to answer. 403 * @param videoState The video state in which to answer the call. 404 */ 405 public void answer(int videoState) { 406 mInCallAdapter.answerCall(mTelecomCallId, videoState); 407 } 408 409 /** 410 * Instructs this {@link #STATE_RINGING} {@code Call} to reject. 411 * 412 * @param rejectWithMessage Whether to reject with a text message. 413 * @param textMessage An optional text message with which to respond. 414 */ 415 public void reject(boolean rejectWithMessage, String textMessage) { 416 mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage); 417 } 418 419 /** 420 * Instructs this {@code Call} to disconnect. 421 */ 422 public void disconnect() { 423 mInCallAdapter.disconnectCall(mTelecomCallId); 424 } 425 426 /** 427 * Instructs this {@code Call} to go on hold. 428 */ 429 public void hold() { 430 mInCallAdapter.holdCall(mTelecomCallId); 431 } 432 433 /** 434 * Instructs this {@link #STATE_HOLDING} call to release from hold. 435 */ 436 public void unhold() { 437 mInCallAdapter.unholdCall(mTelecomCallId); 438 } 439 440 /** 441 * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone. 442 * 443 * Any other currently playing DTMF tone in the specified call is immediately stopped. 444 * 445 * @param digit A character representing the DTMF digit for which to play the tone. This 446 * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. 447 */ 448 public void playDtmfTone(char digit) { 449 mInCallAdapter.playDtmfTone(mTelecomCallId, digit); 450 } 451 452 /** 453 * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone 454 * currently playing. 455 * 456 * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is 457 * currently playing, this method will do nothing. 458 */ 459 public void stopDtmfTone() { 460 mInCallAdapter.stopDtmfTone(mTelecomCallId); 461 } 462 463 /** 464 * Instructs this {@code Call} to continue playing a post-dial DTMF string. 465 * 466 * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, 467 * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. 468 * 469 * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this 470 * {@code Call} will temporarily pause playing the tones for a pre-defined period of time. 471 * 472 * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this 473 * {@code Call} will pause playing the tones and notify listeners via 474 * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app 475 * should display to the user an indication of this state and an affordance to continue 476 * the postdial sequence. When the user decides to continue the postdial sequence, the in-call 477 * app should invoke the {@link #postDialContinue(boolean)} method. 478 * 479 * @param proceed Whether or not to continue with the post-dial sequence. 480 */ 481 public void postDialContinue(boolean proceed) { 482 mInCallAdapter.postDialContinue(mTelecomCallId, proceed); 483 } 484 485 /** 486 * Notifies this {@code Call} that an account has been selected and to proceed with placing 487 * an outgoing call. Optionally sets this account as the default account. 488 */ 489 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) { 490 mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault); 491 492 } 493 494 /** 495 * Instructs this {@code Call} to enter a conference. 496 * 497 * @param callToConferenceWith The other call with which to conference. 498 */ 499 public void conference(Call callToConferenceWith) { 500 if (callToConferenceWith != null) { 501 mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId); 502 } 503 } 504 505 /** 506 * Instructs this {@code Call} to split from any conference call with which it may be 507 * connected. 508 */ 509 public void splitFromConference() { 510 mInCallAdapter.splitFromConference(mTelecomCallId); 511 } 512 513 /** 514 * Merges the calls within this conference. See {@link PhoneCapabilities#MERGE_CONFERENCE}. 515 */ 516 public void mergeConference() { 517 mInCallAdapter.mergeConference(mTelecomCallId); 518 } 519 520 /** 521 * Swaps the calls within this conference. See {@link PhoneCapabilities#SWAP_CONFERENCE}. 522 */ 523 public void swapConference() { 524 mInCallAdapter.swapConference(mTelecomCallId); 525 } 526 527 /** 528 * Obtains the parent of this {@code Call} in a conference, if any. 529 * 530 * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a 531 * child of any conference {@code Call}s. 532 */ 533 public Call getParent() { 534 if (mParentId != null) { 535 return mPhone.internalGetCallByTelecomId(mParentId); 536 } 537 return null; 538 } 539 540 /** 541 * Obtains the children of this conference {@code Call}, if any. 542 * 543 * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty 544 * {@code List} otherwise. 545 */ 546 public List<Call> getChildren() { 547 if (!mChildrenCached) { 548 mChildrenCached = true; 549 mChildren.clear(); 550 551 for(String id : mChildrenIds) { 552 Call call = mPhone.internalGetCallByTelecomId(id); 553 if (call == null) { 554 // At least one child was still not found, so do not save true for "cached" 555 mChildrenCached = false; 556 } else { 557 mChildren.add(call); 558 } 559 } 560 } 561 562 return mUnmodifiableChildren; 563 } 564 565 /** 566 * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference. 567 * 568 * @return The list of conferenceable {@code Call}s. 569 */ 570 public List<Call> getConferenceableCalls() { 571 return mUnmodifiableConferenceableCalls; 572 } 573 574 /** 575 * Obtains the state of this {@code Call}. 576 * 577 * @return A state value, chosen from the {@code STATE_*} constants. 578 */ 579 public int getState() { 580 return mState; 581 } 582 583 /** 584 * Obtains a list of canned, pre-configured message responses to present to the user as 585 * ways of rejecting this {@code Call} using via a text message. 586 * 587 * @see #reject(boolean, String) 588 * 589 * @return A list of canned text message responses. 590 */ 591 public List<String> getCannedTextResponses() { 592 return mCannedTextResponses; 593 } 594 595 /** 596 * Obtains an object that can be used to display video from this {@code Call}. 597 * 598 * @return An {@code Call.VideoCall}. 599 * @hide 600 */ 601 public InCallService.VideoCall getVideoCall() { 602 return mVideoCall; 603 } 604 605 /** 606 * Obtains an object containing call details. 607 * 608 * @return A {@link Details} object. Depending on the state of the {@code Call}, the 609 * result may be {@code null}. 610 */ 611 public Details getDetails() { 612 return mDetails; 613 } 614 615 /** 616 * Adds a listener to this {@code Call}. 617 * 618 * @param listener A {@code Listener}. 619 */ 620 public void addListener(Listener listener) { 621 mListeners.add(listener); 622 } 623 624 /** 625 * Removes a listener from this {@code Call}. 626 * 627 * @param listener A {@code Listener}. 628 */ 629 public void removeListener(Listener listener) { 630 if (listener != null) { 631 mListeners.remove(listener); 632 } 633 } 634 635 /** {@hide} */ 636 Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) { 637 mPhone = phone; 638 mTelecomCallId = telecomCallId; 639 mInCallAdapter = inCallAdapter; 640 mState = STATE_NEW; 641 } 642 643 /** {@hide} */ 644 final String internalGetCallId() { 645 return mTelecomCallId; 646 } 647 648 /** {@hide} */ 649 final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) { 650 // First, we update the internal state as far as possible before firing any updates. 651 Details details = new Details( 652 parcelableCall.getHandle(), 653 parcelableCall.getHandlePresentation(), 654 parcelableCall.getCallerDisplayName(), 655 parcelableCall.getCallerDisplayNamePresentation(), 656 parcelableCall.getAccountHandle(), 657 parcelableCall.getCapabilities(), 658 parcelableCall.getProperties(), 659 parcelableCall.getDisconnectCause(), 660 parcelableCall.getConnectTimeMillis(), 661 parcelableCall.getGatewayInfo(), 662 parcelableCall.getVideoState(), 663 parcelableCall.getStatusHints(), 664 parcelableCall.getExtras()); 665 boolean detailsChanged = !Objects.equals(mDetails, details); 666 if (detailsChanged) { 667 mDetails = details; 668 } 669 670 boolean cannedTextResponsesChanged = false; 671 if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null 672 && !parcelableCall.getCannedSmsResponses().isEmpty()) { 673 mCannedTextResponses = 674 Collections.unmodifiableList(parcelableCall.getCannedSmsResponses()); 675 } 676 677 boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall()); 678 if (videoCallChanged) { 679 mVideoCall = parcelableCall.getVideoCall(); 680 } 681 682 int state = stateFromParcelableCallState(parcelableCall.getState()); 683 boolean stateChanged = mState != state; 684 if (stateChanged) { 685 mState = state; 686 } 687 688 String parentId = parcelableCall.getParentCallId(); 689 boolean parentChanged = !Objects.equals(mParentId, parentId); 690 if (parentChanged) { 691 mParentId = parentId; 692 } 693 694 List<String> childCallIds = parcelableCall.getChildCallIds(); 695 boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds); 696 if (childrenChanged) { 697 mChildrenIds.clear(); 698 mChildrenIds.addAll(parcelableCall.getChildCallIds()); 699 mChildrenCached = false; 700 } 701 702 List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds(); 703 List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size()); 704 for (String otherId : conferenceableCallIds) { 705 if (callIdMap.containsKey(otherId)) { 706 conferenceableCalls.add(callIdMap.get(otherId)); 707 } 708 } 709 710 if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) { 711 mConferenceableCalls.clear(); 712 mConferenceableCalls.addAll(conferenceableCalls); 713 fireConferenceableCallsChanged(); 714 } 715 716 // Now we fire updates, ensuring that any client who listens to any of these notifications 717 // gets the most up-to-date state. 718 719 if (stateChanged) { 720 fireStateChanged(mState); 721 } 722 if (detailsChanged) { 723 fireDetailsChanged(mDetails); 724 } 725 if (cannedTextResponsesChanged) { 726 fireCannedTextResponsesLoaded(mCannedTextResponses); 727 } 728 if (videoCallChanged) { 729 fireVideoCallChanged(mVideoCall); 730 } 731 if (parentChanged) { 732 fireParentChanged(getParent()); 733 } 734 if (childrenChanged) { 735 fireChildrenChanged(getChildren()); 736 } 737 738 // If we have transitioned to DISCONNECTED, that means we need to notify clients and 739 // remove ourselves from the Phone. Note that we do this after completing all state updates 740 // so a client can cleanly transition all their UI to the state appropriate for a 741 // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list. 742 if (mState == STATE_DISCONNECTED) { 743 fireCallDestroyed(); 744 mPhone.internalRemoveCall(this); 745 } 746 } 747 748 /** {@hide} */ 749 final void internalSetPostDialWait(String remaining) { 750 mRemainingPostDialSequence = remaining; 751 firePostDialWait(mRemainingPostDialSequence); 752 } 753 754 /** {@hide} */ 755 final void internalSetDisconnected() { 756 if (mState != Call.STATE_DISCONNECTED) { 757 mState = Call.STATE_DISCONNECTED; 758 fireStateChanged(mState); 759 fireCallDestroyed(); 760 mPhone.internalRemoveCall(this); 761 } 762 } 763 764 private void fireStateChanged(int newState) { 765 for (Listener listener : mListeners) { 766 listener.onStateChanged(this, newState); 767 } 768 } 769 770 private void fireParentChanged(Call newParent) { 771 for (Listener listener : mListeners) { 772 listener.onParentChanged(this, newParent); 773 } 774 } 775 776 private void fireChildrenChanged(List<Call> children) { 777 for (Listener listener : mListeners) { 778 listener.onChildrenChanged(this, children); 779 } 780 } 781 782 private void fireDetailsChanged(Details details) { 783 for (Listener listener : mListeners) { 784 listener.onDetailsChanged(this, details); 785 } 786 } 787 788 private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) { 789 for (Listener listener : mListeners) { 790 listener.onCannedTextResponsesLoaded(this, cannedTextResponses); 791 } 792 } 793 794 private void fireVideoCallChanged(InCallService.VideoCall videoCall) { 795 for (Listener listener : mListeners) { 796 listener.onVideoCallChanged(this, videoCall); 797 } 798 } 799 800 private void firePostDialWait(String remainingPostDialSequence) { 801 for (Listener listener : mListeners) { 802 listener.onPostDialWait(this, remainingPostDialSequence); 803 } 804 } 805 806 private void fireCallDestroyed() { 807 for (Listener listener : mListeners) { 808 listener.onCallDestroyed(this); 809 } 810 } 811 812 private void fireConferenceableCallsChanged() { 813 for (Listener listener : mListeners) { 814 listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls); 815 } 816 } 817 818 private int stateFromParcelableCallState(int parcelableCallState) { 819 switch (parcelableCallState) { 820 case CallState.NEW: 821 return STATE_NEW; 822 case CallState.CONNECTING: 823 return STATE_CONNECTING; 824 case CallState.PRE_DIAL_WAIT: 825 return STATE_PRE_DIAL_WAIT; 826 case CallState.DIALING: 827 return STATE_DIALING; 828 case CallState.RINGING: 829 return STATE_RINGING; 830 case CallState.ACTIVE: 831 return STATE_ACTIVE; 832 case CallState.ON_HOLD: 833 return STATE_HOLDING; 834 case CallState.DISCONNECTED: 835 return STATE_DISCONNECTED; 836 case CallState.ABORTED: 837 return STATE_DISCONNECTED; 838 case CallState.DISCONNECTING: 839 return STATE_DISCONNECTING; 840 default: 841 Log.wtf(this, "Unrecognized CallState %s", parcelableCallState); 842 return STATE_NEW; 843 } 844 } 845} 846