1 /* 2 * Copyright 2023 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 androidx.core.telecom 18 19 import android.net.Uri 20 import android.telecom.PhoneAccountHandle 21 import androidx.annotation.IntDef 22 import androidx.annotation.RequiresApi 23 import androidx.annotation.RestrictTo 24 import androidx.core.telecom.internal.utils.CallAttributesUtils 25 import androidx.core.telecom.internal.utils.Utils 26 import java.util.Objects 27 28 /** 29 * CallAttributes represents a set of properties that define a new Call. Applications should build 30 * an instance of this class and use [CallsManager.addCall] to start a new call with Telecom. 31 * 32 * @param displayName Display name of the person on the other end of the call 33 * @param address Address of the call. Note, this can be extended to a meeting link or a custom 34 * scheme. On sdks 26 & 27, the address should start with the "sip:" prefix (e.g 35 * Uri.parse("sip:")), otherwise the address will be replaced with "sip:" + packageName. 36 * @param direction The direction (Outgoing/Incoming) of the new Call 37 * @param callType Information related to data being transmitted (voice, video, etc. ) 38 * @param callCapabilities Allows a package to opt into capabilities on the telecom side, on a 39 * per-call basis 40 * @param preferredStartingCallEndpoint allows clients to specify a [CallEndpointCompat] to start a 41 * new call on. The [preferredStartingCallEndpoint] should be a value returned from 42 * [CallsManager.getAvailableStartingCallEndpoints]. Once the call is started, Core-Telecom will 43 * switch to the [preferredStartingCallEndpoint] before running the [CallControlScope]. 44 */ 45 public class CallAttributesCompat( 46 public val displayName: CharSequence, 47 public val address: Uri, 48 @Direction public val direction: Int, 49 @CallType public val callType: Int = CALL_TYPE_AUDIO_CALL, 50 @CallCapability public val callCapabilities: Int = SUPPORTS_SET_INACTIVE, 51 public val preferredStartingCallEndpoint: CallEndpointCompat? = null 52 ) { 53 internal var mHandle: PhoneAccountHandle? = null 54 toStringnull55 override fun toString(): String { 56 return "CallAttributes(" + 57 "displayName=[$displayName], " + 58 "address=[$address], " + 59 "direction=[${directionToString()}], " + 60 "callType=[${callTypeToString()}], " + 61 "capabilities=[${capabilitiesToString()}])" 62 } 63 equalsnull64 override fun equals(other: Any?): Boolean { 65 return other is CallAttributesCompat && 66 displayName == other.displayName && 67 address == other.address && 68 direction == other.direction && 69 callType == other.callType && 70 callCapabilities == other.callCapabilities 71 } 72 hashCodenull73 override fun hashCode(): Int { 74 return Objects.hash(displayName, address, direction, callType, callCapabilities) 75 } 76 77 public companion object { 78 @RestrictTo(RestrictTo.Scope.LIBRARY) 79 @Retention(AnnotationRetention.SOURCE) 80 @IntDef(DIRECTION_INCOMING, DIRECTION_OUTGOING) 81 @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) 82 public annotation class Direction 83 84 /** Indicates that the call is an incoming call. */ 85 public const val DIRECTION_INCOMING: Int = 1 86 87 /** Indicates that the call is an outgoing call. */ 88 public const val DIRECTION_OUTGOING: Int = 2 89 90 @RestrictTo(RestrictTo.Scope.LIBRARY) 91 @Retention(AnnotationRetention.SOURCE) 92 @IntDef(CALL_TYPE_AUDIO_CALL, CALL_TYPE_VIDEO_CALL) 93 @Target(AnnotationTarget.TYPE, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.PROPERTY) 94 public annotation class CallType 95 96 /** 97 * Used when answering or dialing a call to indicate that the call does not have a video 98 * component 99 */ 100 public const val CALL_TYPE_AUDIO_CALL: Int = 1 101 102 /** Indicates video transmission is supported */ 103 public const val CALL_TYPE_VIDEO_CALL: Int = 2 104 105 @RestrictTo(RestrictTo.Scope.LIBRARY) 106 @Retention(AnnotationRetention.SOURCE) 107 @IntDef(SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER, flag = true) 108 @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) 109 public annotation class CallCapability 110 111 /** 112 * This call being created can be set to inactive (traditionally referred to as hold). This 113 * means that once a new call goes active, if the active call needs to be held in order to 114 * place or receive an incoming call, the active call will be placed on hold. otherwise, the 115 * active call may be disconnected. 116 */ 117 public const val SUPPORTS_SET_INACTIVE: Int = 1 shl 1 118 119 /** 120 * This call can be streamed from a root device to another device to continue the call 121 * without completely transferring it. The call continues to take place on the source 122 * device, however media and control are streamed to another device. 123 */ 124 public const val SUPPORTS_STREAM: Int = 1 shl 2 125 126 /** This call can be completely transferred from one endpoint to another. */ 127 public const val SUPPORTS_TRANSFER: Int = 1 shl 3 128 } 129 130 @RequiresApi(34) toCallAttributesnull131 internal fun toCallAttributes( 132 phoneAccountHandle: PhoneAccountHandle 133 ): android.telecom.CallAttributes { 134 return CallAttributesUtils.Api34PlusImpl.toTelecomCallAttributes( 135 phoneAccountHandle, 136 direction, 137 displayName, 138 address, 139 callType, 140 callCapabilities 141 ) 142 } 143 directionToStringnull144 private fun directionToString(): String { 145 return if (direction == DIRECTION_OUTGOING) { 146 "Outgoing" 147 } else { 148 "Incoming" 149 } 150 } 151 callTypeToStringnull152 private fun callTypeToString(): String { 153 return if (callType == CALL_TYPE_AUDIO_CALL) { 154 "Audio" 155 } else { 156 "Video" 157 } 158 } 159 hasSupportsSetInactiveCapabilitynull160 internal fun hasSupportsSetInactiveCapability(): Boolean { 161 return Utils.hasCapability(SUPPORTS_SET_INACTIVE, callCapabilities) 162 } 163 hasStreamCapabilitynull164 private fun hasStreamCapability(): Boolean { 165 return Utils.hasCapability(SUPPORTS_STREAM, callCapabilities) 166 } 167 hasTransferCapabilitynull168 private fun hasTransferCapability(): Boolean { 169 return Utils.hasCapability(SUPPORTS_TRANSFER, callCapabilities) 170 } 171 capabilitiesToStringnull172 private fun capabilitiesToString(): String { 173 val sb = StringBuilder() 174 sb.append("[") 175 if (hasSupportsSetInactiveCapability()) { 176 sb.append("SetInactive") 177 } 178 if (hasStreamCapability()) { 179 sb.append(", Stream") 180 } 181 if (hasTransferCapability()) { 182 sb.append(", Transfer") 183 } 184 sb.append("])") 185 return sb.toString() 186 } 187 isOutgoingCallnull188 internal fun isOutgoingCall(): Boolean { 189 return direction == DIRECTION_OUTGOING 190 } 191 isVideoCallnull192 internal fun isVideoCall(): Boolean { 193 return callType == CALL_TYPE_VIDEO_CALL 194 } 195 } 196