电信

新的 Android Telecom Jetpack 库可让您轻松告知平台 状态。您可以在 GitHub

依赖项和权限

首先,打开应用模块 build.gradle 文件,并为 androidx Telecom 模块:

dependencies {
    implementation ("androidx.core:core-telecom:1.0.0-alpha02")
}

在应用清单中,声明您的应用使用 MANAGE_OWN_CALLS 权限:

<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />

注册应用

如需让 Android 知道您的应用,您必须注册该应用及其功能。 告知 Android 您的应用支持哪些功能,例如视频通话、通话、 流式传输和保持通话此信息很重要,因此 Android 可以 以便与应用的功能配合使用

 private val callsManager = CallsManager(context)

var capabilities: @CallsManager.Companion.Capability Int =
    CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING

callsManager.registerAppWithTelecom(capabilities)

平台集成

任何调用应用的两种最常见的通话场景是来电 和去电。要正确注册呼叫的方向和 使用下列 API 以适当的方式通知用户。

注册电话会议

以下示例演示了如何注册来电:

companion object {
  const val APP_SCHEME = "MyCustomScheme"
  const val ALL_CALL_CAPABILITIES = (CallAttributes.SUPPORTS_SET_INACTIVE
    or CallAttributes.SUPPORTS_STREAM or CallAttributes.SUPPORTS_TRANSFER)

  const val INCOMING_NAME = "Luke"
  val INCOMING_URI: Uri = Uri.fromParts(APP_SCHEME, "", "")
  // Define all possible properties for CallAttributes
  val INCOMING_CALL_ATTRIBUTES =
    CallAttributes(
      INCOMING_NAME,
      INCOMING_URI,
      DIRECTION_INCOMING,
      CALL_TYPE_VIDEO_CALL,
      ALL_CALL_CAPABILITIES)
}

callAttributes 对象可以具有以下属性:

  • displayName:来电者、会议或会话的名称。
  • address:调用的地址。请注意,此时间可扩展到会议 链接。
  • direction:通话的方向,例如来电去电
  • callType:与要传输的数据相关的信息,如视频 和音频。
  • callCapabilities:一个对象,用于指定调用的功能。

callCapabilities 对象可以具有以下属性:

  • streaming:指示通话是否支持将音频流式传输到另一个通话 采用 Android 的手机。
  • transfer:指示是否可以转接来电。
  • hold:指示是否可以将通话置于保持状态。

添加通话

如果设备不支持,addCall() 方法会返回异常 还是在设置通话时出现错误。

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onIsCallAnswered, // Watch needs to know if it can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
        callControlScope = this
    }
}

接听来电

拨出电话后,您必须接听或拒绝来电。这个 示例演示了如何接听来电:

when (answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {

    }
}

如果另一个调用正在进行中,answer() 将返回 CallControlResult.Error,说明来电为何无人接听。在 在这种情况下,用户需要将另一个通话置于保持状态。

拒接来电

要拒接来电,请使用 DisconnectCause.Rejected 断开通话连接。

fun onRejectCall(){
    coroutineScope.launch {
        callControlScope?.let {
            it.disconnect(DisconnectCause(DisconnectCause.REJECTED))
        }
    }
}

去电

拨出电话时,一旦远程方应答,您必须设置 调用 active,让平台知道调用正在进行中:

when (setActive()) {
    is CallControlResult.Success -> {
        onIsCallActive()
    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

将通话置于保持状态

如果您的通话应用支持保持通话,请使用 setInActive 告知 您的通话处于非活动状态的平台,并且麦克风和摄像头可以自由 被其他应用使用:

when (setInActive()) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

断开连接

要断开通话连接,请通过提供 正当原因:

coroutineScope.launch {
    callControlScope?.disconnect(DisconnectCause(DisconnectCause.LOCAL))
}

转接音频

在通话期间,用户有时会在设备(例如音箱)之间切换 手机听筒或蓝牙设备。使用 availableEndpointscurrentCallEndpoint API,用于获取 以及哪台设备处于活动状态

此示例将两个流程相结合,以创建向用户显示 设备列表以及哪个处于活动状态:

availableEndpoint = combine(callControlScope.availableEndpoints,
    callControlScope.currentCallEndpoint) {
    availableDevices: List<CallEndpoint>, activeDevice : CallEndpoint ->
    availableDevices.map {
        EndPointUI(
            isActive = activeDevice.endpointName == it.endpointName, it
        )
    }
}

如需更改活跃设备,请将 requestEndpointChange 与 “CallEndpoint”。

coroutineScope.launch {
     callControlScope?.requestEndpointChange(callEndpoint)
}

前台支持

Telecom 库支持前台。此库使用 ConnectionService(适用于搭载 Android 13 及更低版本的设备)。对于 Android 14 及 则使用 foregroundtypes 麦克风和摄像头正确 支持前台服务详细了解前台服务

根据前台要求,应用必须发布一条通知 以便用户知道应用正在前台运行。

为确保您的应用获得前台执行优先级,请创建一个 通知。前台优先级 在应用终止通话或通知不再有效时移除 有效。

is TelecomCall.Registered -> {
    val notification = createNotification(call)
    notificationManager.notify(TELECOM_NOTIFICATION_ID, notification)
}

Surface 支持

手表具有通用的端点接收器应用。此应用提供了 用户使用基本界面(例如接听、拒绝和断开连接) 调用。应用通过实现 lambda 函数来支持这些操作 用于告知平台您已对相应设备执行了相应操作。

如果您的 lambda 函数会在 5 秒后超时,并且事务会失败, 应用没有响应。

callsManager.addCall(
        attributes,
        onIsCallAnswered, // Watch/Auto need to know if they can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
//Call Scope
}
/**
  *  Can the call be successfully answered??
  *  TIP: Check the connection/call state to see if you can answer a call
  *  Example you may need to wait for another call to hold.
  **/
val onIsCallAnswered: suspend(type: Int) -> Unit = {}

/**
  * Can the call perform a disconnect
  */
val onIsCallDisconnected: suspend (cause: DisconnectCause) -> Unit = {}

/**
  *  Check is see if you can make the call active.
  *  Other calls and state might stop us from activating the call
  */
val onIsCallActive: suspend () -> Unit = {
    updateCurrentCall {
    }
}

/**
  * Check to see if you can make the call inactivate
  */
val onIsCallInactive: suspend () -> Unit = {}