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