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.os.ParcelUuid 20 import kotlinx.coroutines.CoroutineScope 21 import kotlinx.coroutines.flow.Flow 22 23 /** 24 * DSL interface to provide and receive updates about a call session. The CallControlScope should be 25 * used to provide updates (via the CallControlScope suspend functions) and receive updates (via the 26 * lambda functions) about the call. The CallControlScope will run for the duration of the call. To 27 * see an example implementation of the CallControlScope, please refer to the sample app on 28 * [github.](https://github.com/android/platform-samples/blob/3856087f7f0fa4901e521f1dc79a30ddfd108f4e/samples/connectivity/telecom/src/main/java/com/example/platform/connectivity/telecom/model/TelecomCallRepository.kt#L122) 29 * 30 * Example usage: 31 * 32 * // initiate a call and control via the CallControlScope mCallsManager.addCall( callAttributes, 33 * onAnswerLambda, onDisconnectLambda, onSetActiveLambda, onSetInActiveLambda ) { // This block 34 * represents the CallControlScope. Once Telecom has added the call to the // system, your 35 * application can start changing the call state (via setActive(), etc.), // collect the flows. 36 * 37 * // Your application should gate ALL CallControlScope suspend functions with UI logic or 38 * // logic that signals the call state is ready to be changed 39 * launch { 40 * when (val res = setActive() ) { 41 * is CallControlResult.Success -> { 42 * // Telecom can place the active 43 * // update your call state and handle UI 44 * } 45 * is CallControlResult.Error -> { 46 * // Telecom cannot set your VoIP call active. Maybe there is an ongoing 47 * // active call that cannot be held. Check the failure code to determine the 48 * // recommended next action 49 * handleErrorCode( res.errorCode ) 50 * } 51 * } 52 * } 53 * } 54 * // Collect updates 55 * launch { 56 * currentCallEndpoint.collect { // access the new [CallEndpoint] here } 57 * } 58 * launch { 59 * availableEndpoints.collect { // access the available [CallEndpoint]s here } 60 * } 61 * launch { 62 * isMuted.collect { // access to the mute state } 63 * } 64 * } 65 * 66 * **Note:** Each [Flow] must be wrapped in an individual launch block or the [Flow] will not be 67 * collected. 68 */ 69 public interface CallControlScope : CoroutineScope { 70 /** 71 * @return the 128-bit universally unique identifier Telecom assigned to this CallControlScope. 72 * This id can be helpful for debugging when dumping the telecom system. 73 */ getCallIdnull74 public fun getCallId(): ParcelUuid 75 76 /** 77 * Inform Telecom that your app wants to make this call active. This method should be called 78 * when either an outgoing call is ready to go active or a held call is ready to go active 79 * again. For incoming calls that are ready to be answered, use [answer]. 80 * 81 * @return Telecom will return [CallControlResult.Success] if your app is able to set the call 82 * active. Otherwise [CallControlResult.Error] will be returned (ex. another call is active 83 * and telecom cannot set this call active until the other call is held or disconnected) with 84 * an error code indicating why setActive failed. 85 */ 86 public suspend fun setActive(): CallControlResult 87 88 /** 89 * Inform Telecom that your app wants to make this call inactive. This the same as hold for two 90 * call endpoints but can be extended to setting a meeting to inactive. 91 * 92 * @return Telecom will return [CallControlResult.Success] if your app is able to set the call 93 * inactive. Otherwise, [CallControlResult.Error] will be returned with an error code 94 * indicating why setInActive failed. 95 */ 96 public suspend fun setInactive(): CallControlResult 97 98 /** 99 * Inform Telecom that your app wants to make this incoming call active. For outgoing calls and 100 * calls that have been placed on hold, use [setActive]. 101 * 102 * @param [callType] that call is to be answered as. 103 * @return Telecom will return [CallControlResult.Success] if your app is able to answer the 104 * call. Otherwise [CallControlResult.Error] will be returned with an error code indicating 105 * why answer failed (ex. another call is active and telecom cannot set this call active until 106 * the other call is held or disconnected). This means that your app cannot answer this call 107 * at this time. 108 */ 109 public suspend fun answer( 110 @CallAttributesCompat.Companion.CallType callType: Int 111 ): CallControlResult 112 113 /** 114 * Inform Telecom that your app wishes to disconnect the call and remove the call from telecom 115 * tracking. 116 * 117 * @param disconnectCause represents the cause for disconnecting the call. The only valid codes 118 * for the [android.telecom.DisconnectCause] passed in are: <ul> 119 * <li>[DisconnectCause#LOCAL]</li> 120 * <li>[DisconnectCause#REMOTE]</li> 121 * <li>[DisconnectCause#REJECTED]</li> 122 * <li>[DisconnectCause#MISSED]</li> </ul> 123 * 124 * @return [CallControlResult.Success] will be returned if Telecom is able to disconnect the 125 * call successfully. Otherwise [CallControlResult.Error] will be returned with an error code 126 * indicating why disconnect failed. 127 */ 128 public suspend fun disconnect( 129 disconnectCause: android.telecom.DisconnectCause 130 ): CallControlResult 131 132 /** 133 * Request a [CallEndpointCompat] change. Clients should not define their own 134 * [CallEndpointCompat] when requesting a change. Instead, the new [endpoint] should be one of 135 * the valid [CallEndpointCompat]s provided by [availableEndpoints]. 136 * 137 * @param endpoint The [CallEndpointCompat] to change to. 138 * @return [CallControlResult.Success] will be returned if Telecom is able to switch to the 139 * requested endpoint successfully. Otherwise, [CallControlResult.Error] will be returned with 140 * an error code indicating why disconnect failed. 141 */ 142 public suspend fun requestEndpointChange(endpoint: CallEndpointCompat): CallControlResult 143 144 /** 145 * Collect the new [CallEndpointCompat] through which call media flows (i.e. speaker, bluetooth, 146 * etc.). 147 */ 148 public val currentCallEndpoint: Flow<CallEndpointCompat> 149 150 /** Collect the set of available [CallEndpointCompat]s reported by Telecom. */ 151 public val availableEndpoints: Flow<List<CallEndpointCompat>> 152 153 /** 154 * Collect the current mute state of the call. This Flow is updated every time the mute state 155 * changes. 156 */ 157 public val isMuted: Flow<Boolean> 158 } 159