Core-Telecom

Core-Telecom ライブラリは、堅牢で一貫性のある API セットを提供することで、通話アプリケーションを Android プラットフォームに統合するプロセスを効率化します。

実際の実装を確認する場合は、GitHub でサンプル アプリケーションをご覧ください。

Core-Telecom を設定する

アプリの build.gradle ファイルに androidx.core:core-telecom 依存関係を追加します。

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

AndroidManifest.xml で次のように MANAGE_OWN_CALLS 権限を宣言します。

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

アプリを登録する

CallsManager を使用して通話アプリを Android に登録し、システムへの通話の追加を開始します。登録時に、アプリの機能(オーディオ、動画のサポートなど)を指定します。

val callsManager = CallsManager(context)

val capabilities: @CallsManager.Companion.Capability Int =
    (CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)

callsManager.registerAppWithTelecom(capabilities)

通話管理

Core-Telecom API を使用して、通話のライフサイクルを作成、管理します。

電話番号を作成する

CallAttributesCompat オブジェクトは、一意の呼び出しのプロパティを定義します。この呼び出しには次の特性があります。

  • displayName: 発信者の名前。
  • address: 通話アドレス(電話番号、会議リンクなど)。
  • direction: 着信または発信。
  • callType: 音声または動画。
  • callCapabilities: 転送と保留をサポートします。

着信を作成する方法の例を次に示します。

fun createIncomingCallAttributes(
    callerName: String,
    callerNumber: String,
    isVideoCall: Boolean): CallAttributesCompat {
    val addressUri = Uri.parse("YourAppScheme:$callerNumber")

    return CallAttributesCompat(
        displayName = callerName,
        address = addressUri,
        direction = CallAttributesCompat.DIRECTION_INCOMING,
        callType = if (isVideoCall) {
            CallAttributesCompat.CALL_TYPE_VIDEO_CALL
        } else {
            CallAttributesCompat.CALL_TYPE_AUDIO_CALL
        },
        callCapabilities = CallAttributesCompat.SUPPORTS_SET_INACTIVE
    )
}

通話を追加する

callsManager.addCall を CallAttributesCompat およびコールバックとともに使用して、システムに新しい呼び出しを追加し、リモート サーフェスの更新を管理します。addCall ブロック内の callControlScope は、主にアプリが通話状態を移行して音声の更新を受信できるようにします。

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onAnswerCall, // Watch needs to know if it can answer the call.
        onSetCallDisconnected,
        onSetCallActive,
        onSetCallInactive
    ) {
        // The call was successfully added once this scope runs.
        callControlScope = this
    }
}
catch(addCallException: Exception){
   // Handle the addCall failure.
}

着信に応答する

CallControlScope 以内に着信に応答します。

when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> { /* Call answered */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

通話を拒否する

CallControlScope 内の DisconnectCause.REJECTED を使用して disconnect() で通話を拒否します。

disconnect(DisconnectCause(DisconnectCause.REJECTED))

発信中の通話をアクティブにする

相手が応答したら、発信通話をアクティブに設定します。

when (val result = setActive()) {
    is CallControlResult.Success -> { /* Call active */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

通話を保留にする

setInactive() を使用して通話を保留にします。

when (val result = setInactive()) {
    is CallControlResult.Success -> { /* Call on hold */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

通話を切る

DisconnectCause を使用して disconnect() で通話を切断します。

disconnect(DisconnectCause(DisconnectCause.LOCAL))

通話音声エンドポイントを管理する

CallControlScope 内の currentCallEndpoint、availableEndpoints、isMuted Flow を使用して、音声エンドポイントをモニタリングして管理します。Telecom を使用している場合は、AudioManager#setCommunicationDevice API または AudioManager#startBluetoothSco API を使用して音声ルートを管理しないでください。通話で音声の問題が発生します。

fun observeAudioStateChanges(callControlScope: CallControlScope) {
    with(callControlScope) {
        launch { currentCallEndpoint.collect { /* Update UI */ } }
        launch { availableEndpoints.collect { /* Update UI */ } }
        launch { isMuted.collect { /* Handle mute state */ } }
    }
}

requestEndpointChange() を使用してアクティブなオーディオ機器を変更します。

coroutineScope.launch {
     callControlScope.requestEndpointChange(callEndpoint)
}

フォアグラウンド サポート

このライブラリは、フォアグラウンド サポートに Android 13(API レベル 33)以前では ConnectionService を、Android 14(API レベル 34)以降ではフォアグラウンド サービス タイプを使用します。

アプリがバックグラウンドにあるときに通話をアクティブに保つには、フォアグラウンド Service(LifecycleService など)内で CallsManager をホストし、AndroidManifest.xml で phoneCall フォアグラウンド サービスのタイプを宣言します。

<service
    android:name=".TelecomVoipService"
    android:foregroundServiceType="phoneCall" />

フォアグラウンドの要件の一環として、アプリは NotificationCompat.CallStyle 通知を投稿して、フォアグラウンドで通話がアクティブであることをユーザーに知らせる必要があります。アプリがフォアグラウンド実行の優先度を取得できるようにするには、プラットフォームで呼び出しを追加したら、startForeground を使用してサービスをフォアグラウンドに昇格させます。

startForeground(
    notificationId,
    notification,
    ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
)

詳しくは、フォアグラウンド サービスをご覧ください。

リモート Surface サポート

リモート デバイス(スマートウォッチ、Bluetooth ヘッドセット、Android Auto)は、スマートフォンを直接操作しなくても通話管理が可能です。アプリは、これらのデバイスによって開始されたアクションを処理するために、CallsManager.addCall に提供されるコールバック ラムダ(onAnswerCall、onSetCallDisconnected、onSetCallActive、onSetCallInactive)を実装する必要があります。

リモート アクションが発生すると、対応するラムダが呼び出されます。

ラムダが正常に完了すると、コマンドが処理されたことが通知されます。コマンドに従えない場合、ラムダは例外をスローします。

適切に実装することで、さまざまなデバイスでシームレスな通話制御が可能になります。さまざまなリモコンの表面で徹底的にテストします。

電話番号表示オプション

このライブラリは、通話の状態と音声ルートの管理に加えて、通話拡張機能もサポートしています。通話拡張機能は、Android Auto などのリモート サーフェスでより豊かな通話体験を実現するためにアプリが実装できるオプション機能です。会議室、通話のミュート、通話アイコンの追加などの機能があります。アプリが拡張機能を実装すると、アプリが提供する情報は、UI でこれらの拡張機能の表示をサポートするすべての接続済みデバイスと同期されます。つまり、これらの機能はリモート デバイスでも利用可能になり、ユーザーが操作できるようになります。

拡張機能を使用して通話を作成する

通話を作成する際に、CallsManager.addCall を使用して通話を作成する代わりに、CallsManager.addCallWithExtensions を使用できます。これにより、アプリは ExtensionInitializationScope という別のスコープにアクセスできるようになります。このスコープにより、アプリケーションはサポートするオプションの拡張機能のセットを初期化できます。また、このスコープには、拡張機能の交換と初期化が完了した後に CallControlScope をアプリに返す追加のメソッド onCall が用意されています。

scope.launch {
    mCallsManager.addCallWithExtensions(
        attributes,
        onAnswer,
        onDisconnect,
        onSetActive,
        onSetInactive
    ) {
        // Initialize extension-specific code...

        // After the call has been initialized, perform in-call actions
        onCall {
            // Example: process call state updates
            callStateFlow.onEach { newState ->
                // handle call state updates and notify telecom
            }.launchIn(this)

            // Use initialized extensions...
        }
    }
}

サポート通話の参加者

アプリが会議やグループ通話の通話参加者をサポートしている場合は、addParticipantExtension を使用してこの拡張機能のサポートを宣言し、関連する API を使用して参加者が変更されたときにリモート サーフェスを更新します。

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Notifies Jetpack that this app supports the participant
        // extension and provides the initial participants state in the call.
        val participantExtension = addParticipantExtension(
            initialParticipants,
            initialActiveParticipant
        )

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // Example: update remote surfaces when the call participants change
            participantsFlow.onEach { newParticipants ->
                participantExtension.updateParticipants(newParticipants)
            }.launchIn(this)
        }
    }

ParticipantExtension#updateActiveParticipant を使用して、通話に参加しているユーザーをリモート サーフェスに通知するとともに、アクティブな参加者を更新することもできます。

通話の参加者に関連するオプションのアクションもサポートされています。アプリは ParticipantExtension#addRaiseHandSupport を使用して、通話で参加者が挙手したことをサポートし、他の参加者が挙手しているかどうかを確認できます。

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Notifies Jetpack that this app supports the participant
        // extension and provides the initial list of participants in the call.
        val participantExtension = addParticipantExtension(initialParticipants)
        // Notifies Jetpack that this app supports the notion of participants
        // being able to raise and lower their hands.
        val raiseHandState = participantExtension.addRaiseHandSupport(
                initialRaisedHands
            ) { onHandRaisedStateChanged ->
                // handle this user's raised hand state changed updates from
                // remote surfaces.
            }

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // Example: update remote surfaces when the call participants change
            participantsFlow.onEach { newParticipants ->
                participantExtension.updateParticipants(newParticipants)
            }.launchIn(this)
            // notify remote surfaces of which of the participants have their
            // hands raised
            raisedHandsFlow.onEach { newRaisedHands ->
                raiseHandState.updateRaisedHands(newRaisedHands)
            }.launchIn(this)
        }
    }

サポート通話の消音

通話のミュートを使用すると、ユーザーはデバイスのマイクを物理的にミュートすることなく、アプリに通話の発信音声のミュートをリクエストできます。この機能は通話ごとに管理されるため、VOIP 通話がアクティブな間、進行中の携帯電話通話のグローバル ミュート状態を管理する複雑な処理は Jetpack が行います。これにより、複数通話のシナリオで発信音声のミュートがエラーを起こしにくくなり、通話のミュートが有効になっていることに気づかずにユーザーが話しているときに「話していますか?」という表示などの便利な機能も利用できるようになります。

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Add support for locally silencing the call's outgoing audio and
        // register a handler for when the user changes the call silence state
        // from a remote surface.
        val callSilenceExtension = addLocalCallSilenceExtension(
            initialCallSilenceState = false
        ) { newCallSilenceStateRequest ->
            // handle the user's request to enable/disable call silence from
            // a remote surface
        }

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // When the call's call silence state changes, update remote
            // surfaces of the new state.
            callSilenceState.onEach { isSilenced ->
                callSilenceExtension.updateIsLocallySilenced(isSilenced)
            }.launchIn(this)
        }
    }

サポート通話アイコン

通話アイコンを使用すると、アプリは通話中にリモート サーフェスに表示される通話を表すカスタム アイコンを指定できます。このアイコンは、通話のライフサイクル全体で更新することもできます。

をご覧ください。
mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Add support for a custom call icon to be displayed during the
        // lifetime of the call.
        val callIconExtension = addCallIconExtension(
            initialCallIconUri = initialUri
        )

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // When the call's icon changes, update remote surfaces by providing
            // the new URI.
            callIconUri.onEach { newIconUri ->
                callIconExtension.updateCallIconUri(newIconUri)
            }.launchIn(this)
        }
    }

システム通話ログに追加する

アプリの VoIP 通話をシステム通話履歴に追加して、システム ダイヤルに表示し、ユーザーがそこから折り返し電話をかけられるようにすることができます。詳しくは、統合された通話履歴をご覧ください。