1/* 2 * Copyright (C) 2013 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 com.android.incallui; 18 19import com.android.contacts.common.CallUtil; 20 21import android.content.Context; 22import android.net.Uri; 23import android.telecom.CallProperties; 24import android.telecom.DisconnectCause; 25import android.telecom.PhoneCapabilities; 26import android.telecom.GatewayInfo; 27import android.telecom.InCallService.VideoCall; 28import android.telecom.PhoneAccountHandle; 29import android.telecom.VideoProfile; 30 31import java.util.ArrayList; 32import java.util.List; 33import java.util.Locale; 34 35/** 36 * Describes a single call and its state. 37 */ 38public final class Call { 39 /* Defines different states of this call */ 40 public static class State { 41 public static final int INVALID = 0; 42 public static final int IDLE = 1; /* The call is idle. Nothing active */ 43 public static final int ACTIVE = 2; /* There is an active call */ 44 public static final int INCOMING = 3; /* A normal incoming phone call */ 45 public static final int CALL_WAITING = 4; /* Incoming call while another is active */ 46 public static final int DIALING = 5; /* An outgoing call during dial phase */ 47 public static final int REDIALING = 6; /* Subsequent dialing attempt after a failure */ 48 public static final int ONHOLD = 7; /* An active phone call placed on hold */ 49 public static final int DISCONNECTING = 8; /* A call is being ended. */ 50 public static final int DISCONNECTED = 9; /* State after a call disconnects */ 51 public static final int CONFERENCED = 10; /* Call part of a conference call */ 52 public static final int PRE_DIAL_WAIT = 11; /* Waiting for user before outgoing call */ 53 public static final int CONNECTING = 12; /* Waiting for Telecomm broadcast to finish */ 54 55 public static boolean isConnectingOrConnected(int state) { 56 switch(state) { 57 case ACTIVE: 58 case INCOMING: 59 case CALL_WAITING: 60 case CONNECTING: 61 case DIALING: 62 case REDIALING: 63 case ONHOLD: 64 case CONFERENCED: 65 return true; 66 default: 67 } 68 return false; 69 } 70 71 public static boolean isDialing(int state) { 72 return state == DIALING || state == REDIALING; 73 } 74 75 public static String toString(int state) { 76 switch (state) { 77 case INVALID: 78 return "INVALID"; 79 case IDLE: 80 return "IDLE"; 81 case ACTIVE: 82 return "ACTIVE"; 83 case INCOMING: 84 return "INCOMING"; 85 case CALL_WAITING: 86 return "CALL_WAITING"; 87 case DIALING: 88 return "DIALING"; 89 case REDIALING: 90 return "REDIALING"; 91 case ONHOLD: 92 return "ONHOLD"; 93 case DISCONNECTING: 94 return "DISCONNECTING"; 95 case DISCONNECTED: 96 return "DISCONNECTED"; 97 case CONFERENCED: 98 return "CONFERENCED"; 99 case PRE_DIAL_WAIT: 100 return "PRE_DIAL_WAIT"; 101 case CONNECTING: 102 return "CONNECTING"; 103 default: 104 return "UNKNOWN"; 105 } 106 } 107 } 108 109 /** 110 * Defines different states of session modify requests, which are used to upgrade to video, or 111 * downgrade to audio. 112 */ 113 public static class SessionModificationState { 114 public static final int NO_REQUEST = 0; 115 public static final int WAITING_FOR_RESPONSE = 1; 116 public static final int REQUEST_FAILED = 2; 117 public static final int RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3; 118 } 119 120 private static final String ID_PREFIX = Call.class.getSimpleName() + "_"; 121 private static int sIdCounter = 0; 122 123 private android.telecom.Call.Listener mTelecommCallListener = 124 new android.telecom.Call.Listener() { 125 @Override 126 public void onStateChanged(android.telecom.Call call, int newState) { 127 update(); 128 } 129 130 @Override 131 public void onParentChanged(android.telecom.Call call, 132 android.telecom.Call newParent) { 133 update(); 134 } 135 136 @Override 137 public void onChildrenChanged(android.telecom.Call call, 138 List<android.telecom.Call> children) { 139 update(); 140 } 141 142 @Override 143 public void onDetailsChanged(android.telecom.Call call, 144 android.telecom.Call.Details details) { 145 update(); 146 } 147 148 @Override 149 public void onCannedTextResponsesLoaded(android.telecom.Call call, 150 List<String> cannedTextResponses) { 151 update(); 152 } 153 154 @Override 155 public void onPostDialWait(android.telecom.Call call, 156 String remainingPostDialSequence) { 157 update(); 158 } 159 160 @Override 161 public void onVideoCallChanged(android.telecom.Call call, 162 VideoCall videoCall) { 163 update(); 164 } 165 166 @Override 167 public void onCallDestroyed(android.telecom.Call call) { 168 call.removeListener(mTelecommCallListener); 169 } 170 171 @Override 172 public void onConferenceableCallsChanged(android.telecom.Call call, 173 List<android.telecom.Call> conferenceableCalls) { 174 update(); 175 } 176 }; 177 178 private final android.telecom.Call mTelecommCall; 179 private final String mId; 180 private int mState = State.INVALID; 181 private DisconnectCause mDisconnectCause; 182 private int mSessionModificationState; 183 private final List<String> mChildCallIds = new ArrayList<>(); 184 185 private InCallVideoCallListener mVideoCallListener; 186 187 public Call(android.telecom.Call telecommCall) { 188 mTelecommCall = telecommCall; 189 mId = ID_PREFIX + Integer.toString(sIdCounter++); 190 updateFromTelecommCall(); 191 mTelecommCall.addListener(mTelecommCallListener); 192 } 193 194 public android.telecom.Call getTelecommCall() { 195 return mTelecommCall; 196 } 197 198 private void update() { 199 int oldState = getState(); 200 updateFromTelecommCall(); 201 if (oldState != getState() && getState() == Call.State.DISCONNECTED) { 202 CallList.getInstance().onDisconnect(this); 203 } else { 204 CallList.getInstance().onUpdate(this); 205 } 206 } 207 208 private void updateFromTelecommCall() { 209 Log.d(this, "updateFromTelecommCall: " + mTelecommCall); 210 setState(translateState(mTelecommCall.getState())); 211 setDisconnectCause(mTelecommCall.getDetails().getDisconnectCause()); 212 213 if (mTelecommCall.getVideoCall() != null) { 214 if (mVideoCallListener == null) { 215 mVideoCallListener = new InCallVideoCallListener(this); 216 } 217 mTelecommCall.getVideoCall().setVideoCallListener(mVideoCallListener); 218 } 219 220 mChildCallIds.clear(); 221 for (int i = 0; i < mTelecommCall.getChildren().size(); i++) { 222 mChildCallIds.add( 223 CallList.getInstance().getCallByTelecommCall( 224 mTelecommCall.getChildren().get(i)).getId()); 225 } 226 } 227 228 private static int translateState(int state) { 229 switch (state) { 230 case android.telecom.Call.STATE_CONNECTING: 231 return Call.State.CONNECTING; 232 case android.telecom.Call.STATE_PRE_DIAL_WAIT: 233 return Call.State.PRE_DIAL_WAIT; 234 case android.telecom.Call.STATE_DIALING: 235 case android.telecom.Call.STATE_NEW: 236 return Call.State.DIALING; 237 case android.telecom.Call.STATE_RINGING: 238 return Call.State.INCOMING; 239 case android.telecom.Call.STATE_ACTIVE: 240 return Call.State.ACTIVE; 241 case android.telecom.Call.STATE_HOLDING: 242 return Call.State.ONHOLD; 243 case android.telecom.Call.STATE_DISCONNECTED: 244 return Call.State.DISCONNECTED; 245 case android.telecom.Call.STATE_DISCONNECTING: 246 return Call.State.DISCONNECTING; 247 default: 248 return Call.State.INVALID; 249 } 250 } 251 252 public String getId() { 253 return mId; 254 } 255 256 public String getNumber() { 257 if (mTelecommCall.getDetails().getGatewayInfo() != null) { 258 return mTelecommCall.getDetails().getGatewayInfo() 259 .getOriginalAddress().getSchemeSpecificPart(); 260 } 261 return getHandle() == null ? null : getHandle().getSchemeSpecificPart(); 262 } 263 264 public Uri getHandle() { 265 return mTelecommCall.getDetails().getHandle(); 266 } 267 268 public int getState() { 269 if (mTelecommCall.getParent() != null) { 270 return State.CONFERENCED; 271 } else { 272 return mState; 273 } 274 } 275 276 public void setState(int state) { 277 mState = state; 278 } 279 280 public int getNumberPresentation() { 281 return getTelecommCall().getDetails().getHandlePresentation(); 282 } 283 284 public int getCnapNamePresentation() { 285 return getTelecommCall().getDetails().getCallerDisplayNamePresentation(); 286 } 287 288 public String getCnapName() { 289 return getTelecommCall().getDetails().getCallerDisplayName(); 290 } 291 292 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */ 293 public DisconnectCause getDisconnectCause() { 294 if (mState == State.DISCONNECTED || mState == State.IDLE) { 295 return mDisconnectCause; 296 } 297 298 return new DisconnectCause(DisconnectCause.UNKNOWN); 299 } 300 301 public void setDisconnectCause(DisconnectCause disconnectCause) { 302 mDisconnectCause = disconnectCause; 303 } 304 305 /** Returns the possible text message responses. */ 306 public List<String> getCannedSmsResponses() { 307 return mTelecommCall.getCannedTextResponses(); 308 } 309 310 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */ 311 public boolean can(int capabilities) { 312 int supportedCapabilities = mTelecommCall.getDetails().getCallCapabilities(); 313 314 if ((capabilities & PhoneCapabilities.MERGE_CONFERENCE) != 0) { 315 // We allow you to merge if the capabilities allow it or if it is a call with 316 // conferenceable calls. 317 if (mTelecommCall.getConferenceableCalls().isEmpty() && 318 ((PhoneCapabilities.MERGE_CONFERENCE & supportedCapabilities) == 0)) { 319 // Cannot merge calls if there are no calls to merge with. 320 return false; 321 } 322 capabilities &= ~PhoneCapabilities.MERGE_CONFERENCE; 323 } 324 return (capabilities == (capabilities & mTelecommCall.getDetails().getCallCapabilities())); 325 } 326 327 private boolean hasProperty(int property) { 328 return property == (property & mTelecommCall.getDetails().getCallProperties()); 329 } 330 331 /** Gets the time when the call first became active. */ 332 public long getConnectTimeMillis() { 333 return mTelecommCall.getDetails().getConnectTimeMillis(); 334 } 335 336 public boolean isConferenceCall() { 337 return hasProperty(CallProperties.CONFERENCE); 338 } 339 340 public GatewayInfo getGatewayInfo() { 341 return mTelecommCall.getDetails().getGatewayInfo(); 342 } 343 344 public PhoneAccountHandle getAccountHandle() { 345 return mTelecommCall.getDetails().getAccountHandle(); 346 } 347 348 public VideoCall getVideoCall() { 349 return mTelecommCall.getVideoCall(); 350 } 351 352 public List<String> getChildCallIds() { 353 return mChildCallIds; 354 } 355 356 public String getParentId() { 357 android.telecom.Call parentCall = mTelecommCall.getParent(); 358 if (parentCall != null) { 359 return CallList.getInstance().getCallByTelecommCall(parentCall).getId(); 360 } 361 return null; 362 } 363 364 public int getVideoState() { 365 return mTelecommCall.getDetails().getVideoState(); 366 } 367 368 public boolean isVideoCall(Context context) { 369 return CallUtil.isVideoEnabled(context) && 370 VideoProfile.VideoState.isBidirectional(getVideoState()); 371 } 372 373 public void setSessionModificationState(int state) { 374 boolean hasChanged = mSessionModificationState != state; 375 mSessionModificationState = state; 376 377 if (hasChanged) { 378 update(); 379 } 380 } 381 382 public static boolean areSame(Call call1, Call call2) { 383 if (call1 == null && call2 == null) { 384 return true; 385 } else if (call1 == null || call2 == null) { 386 return false; 387 } 388 389 // otherwise compare call Ids 390 return call1.getId().equals(call2.getId()); 391 } 392 393 public int getSessionModificationState() { 394 return mSessionModificationState; 395 } 396 397 @Override 398 public String toString() { 399 return String.format(Locale.US, "[%s, %s, %s, children:%s, parent:%s, videoState:%d]", 400 mId, 401 State.toString(getState()), 402 PhoneCapabilities.toString(mTelecommCall.getDetails().getCallCapabilities()), 403 mChildCallIds, 404 getParentId(), 405 mTelecommCall.getDetails().getVideoState()); 406 } 407} 408