CallControlScope.kt
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.core.telecom
import android.os.ParcelUuid
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
/**
* DSL interface to provide and receive updates about a call session. The CallControlScope should be
* used to provide updates (via the CallControlScope suspend functions) and receive updates
* (via the lambda functions) about the call. The CallControlScope will run for the duration of the
* call. To see an example implementation of the CallControlScope, please refer to the sample app on
* [github.](https://github.com/android/platform-samples/blob/3856087f7f0fa4901e521f1dc79a30ddfd108f4e/samples/connectivity/telecom/src/main/java/com/example/platform/connectivity/telecom/model/TelecomCallRepository.kt#L122)
*
* Example usage:
*
* // initiate a call and control via the CallControlScope
* mCallsManager.addCall(
* callAttributes,
* onAnswerLambda,
* onDisconnectLambda,
* onSetActiveLambda,
* onSetInActiveLambda
* ) {
* // This block represents the CallControlScope. Once Telecom has added the call to the
* // system, your application can start changing the call state (via setActive(), etc.),
* // collect the flows.
*
*
* // Your application should gate ALL CallControlScope suspend functions with UI logic or
* // logic that signals the call state is ready to be changed
* launch {
* when (val res = setActive() ) {
*
* is CallControlResult.Success -> {
* // Telecom can place the active
* // update your call state and handle UI
* }
*
* is CallControlResult.Error -> {
* // Telecom cannot set your VoIP call active. Maybe there is an ongoing
* // active call that cannot be held. Check the failure code to determine the
* // recommended next action
* handleErrorCode( res.errorCode )
* }
* }
* }
* }
*
* // Collect updates
* launch {
* currentCallEndpoint.collect { // access the new [CallEndpoint] here }
* }
*
* launch {
* availableEndpoints.collect { // access the available [CallEndpoint]s here }
* }
*
* launch {
* isMuted.collect { // access to the mute state }
* }
* }
*
* **Note:** Each [Flow] must be wrapped in an individual launch block or the [Flow] will not be
* collected.
*/
interface CallControlScope : CoroutineScope {
/**
* @return the 128-bit universally unique identifier Telecom assigned to this CallControlScope.
* This id can be helpful for debugging when dumping the telecom system.
*/
fun getCallId(): ParcelUuid
/**
* Inform Telecom that your app wants to make this call active. This method should be called
* when either an outgoing call is ready to go active or a held call is ready to go active
* again. For incoming calls that are ready to be answered, use [answer].
*
* @return Telecom will return [CallControlResult.Success] if your app is able to set the call
* active. Otherwise [CallControlResult.Error] will be returned (ex. another call is active and
* telecom cannot set this call active until the other call is held or disconnected) with an
* error code indicating why setActive failed.
*/
suspend fun setActive(): CallControlResult
/**
* Inform Telecom that your app wants to make this call inactive. This the same as hold for two
* call endpoints but can be extended to setting a meeting to inactive.
*
* @return Telecom will return [CallControlResult.Success] if your app is able to set the call
* inactive. Otherwise, [CallControlResult.Error] will be returned with an error code
* indicating why setInActive failed.
*/
suspend fun setInactive(): CallControlResult
/**
* Inform Telecom that your app wants to make this incoming call active. For outgoing calls
* and calls that have been placed on hold, use [setActive].
*
* @param [callType] that call is to be answered as.
*
* @return Telecom will return [CallControlResult.Success] if your app is able to answer the
* call. Otherwise [CallControlResult.Error] will be returned with an error code indicating
* why answer failed (ex. another call is active and telecom cannot set this call active until
* the other call is held or disconnected). This means that your app cannot answer this call at
* this time.
*/
suspend fun answer(@CallAttributesCompat.Companion.CallType callType: Int): CallControlResult
/**
* Inform Telecom that your app wishes to disconnect the call and remove the call from telecom
* tracking.
*
* @param disconnectCause represents the cause for disconnecting the call. The only valid
* codes for the [android.telecom.DisconnectCause] passed in are:
* <ul>
* <li>[DisconnectCause#LOCAL]</li>
* <li>[DisconnectCause#REMOTE]</li>
* <li>[DisconnectCause#REJECTED]</li>
* <li>[DisconnectCause#MISSED]</li>
* </ul>
*
* @return [CallControlResult.Success] will be returned if Telecom is able to disconnect
* the call successfully. Otherwise [CallControlResult.Error] will be returned with an error
* code indicating why disconnect failed.
*/
suspend fun disconnect(disconnectCause: android.telecom.DisconnectCause): CallControlResult
/**
* Request a [CallEndpointCompat] change. Clients should not define their own [CallEndpointCompat] when
* requesting a change. Instead, the new [endpoint] should be one of the valid [CallEndpointCompat]s
* provided by [availableEndpoints].
*
* @param endpoint The [CallEndpointCompat] to change to.
*
* @return [CallControlResult.Success] will be returned if Telecom is able to switch to the
* requested endpoint successfully. Otherwise, [CallControlResult.Error] will be returned with
* an error code indicating why disconnect failed.
*/
suspend fun requestEndpointChange(endpoint: CallEndpointCompat): CallControlResult
/**
* Collect the new [CallEndpointCompat] through which call media flows (i.e. speaker,
* bluetooth, etc.).
*/
val currentCallEndpoint: Flow<CallEndpointCompat>
/**
* Collect the set of available [CallEndpointCompat]s reported by Telecom.
*/
val availableEndpoints: Flow<List<CallEndpointCompat>>
/**
* Collect the current mute state of the call. This Flow is updated every time the mute state
* changes.
*/
val isMuted: Flow<Boolean>
}