通話アプリを作成する

通話アプリでは、ユーザーがデバイスで音声通話やビデオ通話を発信または着信できます。次のスクリーンショットに示すように、通話アプリでは、デフォルトの電話アプリのインターフェースではなく、アプリ独自のユーザー インターフェースを通話に使用します。

通話アプリの例
独自のインターフェースを使用する通話アプリの例

Android フレームワークに含まれている android.telecom パッケージには、Telecom フレームワークに沿って通話アプリを作成するためのクラスが用意されています。Telecom フレームワークに沿ってアプリを作成することには、次のような利点があります。

  • アプリが、デバイス内のネイティブの Telecom サブシステムと適切に相互運用できます。
  • アプリが、このフレームワークに準拠している他の通話アプリと適切に相互運用できます。
  • アプリでこのフレームワークを使用して、音声や動画のルーティングを管理できます。
  • アプリでこのフレームワークを使用して、自身の通話にフォーカスがあるかどうかを判断できます。

マニフェストの宣言と権限

次の例に示すように、アプリ マニフェストで、アプリが MANAGE_OWN_CALLS 権限を使用することを宣言します。

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

アプリの権限の宣言について詳しくは、権限をご覧ください。

アプリの ConnectionService クラスを実装するクラスを指定しているサービスを宣言する必要があります。Telecom サブシステムでは、サービスをバインドできるようにするために、サービスが BIND_TELECOM_CONNECTION_SERVICE 権限を宣言する必要があります。アプリ マニフェスト内でサービスを宣言する方法を、次の例に示します。

<service android:name="com.example.MyConnectionService"
    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.ConnectionService" />
    </intent-filter>
</service>

サービスを含むアプリ コンポーネントの宣言について詳しくは、アプリ コンポーネントをご覧ください。

接続サービスを実装する

呼び出し元アプリは、Telecom サブシステムがバインドできる ConnectionService クラスの実装を提供する必要があります。ConnectionService の実装では次のメソッドをオーバーライドする必要があります。

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

アプリで placeCall(Uri, Bundle) を呼び出すと、それを受けて Telecom サブシステムがこのメソッドを呼び出して新しい発信を作成します。アプリは Connection クラスの実装(詳細は接続の実装を参照)の新しいインスタンスを返して、その新しい発信を表します。以下の処理を行うことで、発信接続をさらにカスタマイズできます。

onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

このメソッドは、アプリが placeCall(Uri, Bundle) メソッドを呼び出したものの通話を発信できなかった場合に、Telecom サブシステムによって呼び出されます。こうした状況が発生した場合、アプリはユーザーに対して、発信ができなかったことを通知(たとえば、アラート ボックスやトーストを使用)する必要があります。進行中の緊急通報がある場合や、通話の発信前に保留にできない進行中の通話が別のアプリにある場合は通常、アプリから通話を発信できません。

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

アプリが addNewIncomingCall(PhoneAccountHandle, Bundle) メソッドを呼び出して、アプリ内での新しい着信をシステムに通知すると、Telecom サブシステムがこのメソッドを呼び出します。アプリは Connection の実装(詳細は接続の実装を参照)の新しいインスタンスを返して、新しい着信を表します。以下の処理を行うことで、着信接続をさらにカスタマイズできます。

onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

アプリが addNewIncomingCall(PhoneAccountHandle, Bundle) メソッドを呼び出して、新しい着信があったものの着信が許可されていないことを Telecom サブシステムに通知すると、Telecom サブシステムがこのメソッドを呼び出します(詳しくは、通話の制約をご覧ください)。アプリは着信を拒否する必要があります。また、オプションで、不在着信があったことをユーザーに伝える通知を表示できます。

接続を実装する

アプリは Connection のサブクラスを作成して、アプリ内で通話を表す必要があります。実装において、次のメソッドをオーバーライドする必要があります。

onShowIncomingCallUi()

このメソッドは、新しい着信が追加されて、アプリが自身の着信 UI を表示する必要がある場合、Telecom サブシステムによって呼び出されます。

onCallAudioStateChanged(CallAudioState)

このメソッドは、アプリに現在の音声経路またはモードが変更されたことを通知するために、Telecom サブシステムによって呼び出されます。これは、アプリが setAudioRoute(int) メソッドを使用して音声モードを変更したことを受けて呼び出されます。また、システムによって音声経路が変更された場合(Bluetooth ヘッドセットが切断された場合など)にも、このメソッドが呼び出されることがあります。

onHold()

このメソッドは、Telecom サブシステムが通話の保留をリクエストする場合に、Telecom サブシステムによって呼び出されます。このリクエストを受けて、アプリは通話を保留にしたうえで setOnHold() メソッドを呼び出して、通話が保留になっていることをシステムに通知する必要があります。このメソッドは、通話を表示している通話サービス(Android Auto など)が、通話を保留にするというユーザーのリクエストの中継を求めた場合に、Telecom サブシステムによって呼び出されることがあります。また、ユーザーが別のアプリで通話をアクティブにした場合にも呼び出されます。通話サービスについて詳しくは、InCallService をご覧ください。

onUnhold()

このメソッドは、Telecom サブシステムが保留になっている通話の再開をリクエストする場合に、Telecom サブシステムによって呼び出されます。アプリは通話を再開した後で setActive() メソッドを呼び出して、通話が保留状態ではなくなったことをシステムに通知する必要があります。このメソッドは、通話を表示している通話サービス(Android Auto など)が通話再開のリクエストの中継を求めた場合に、Telecom サブシステムによって呼び出されることがあります。通話サービスについて詳しくは、InCallService をご覧ください。

onAnswer()

このメソッドは、着信への応答が必要であることをアプリに通知するために、Telecom サブシステムによって呼び出されます。アプリは通話に応答した後で setActive() メソッドを呼び出して、通話に応答済みであることをシステムに通知する必要があります。このメソッドは、アプリが新しい着信を追加したときに、すでに別のアプリに保留にできない進行中の通話があると、Telecom サブシステムによって呼び出されることがあります。このような場合、アプリに代わって Telecom により着信 UI が表示されます。フレームワークには、応答する通話の動画の状態の指定をサポートするオーバーロード メソッドが用意されています。詳しくは onAnswer(int) をご覧ください。

onReject()

このメソッドは、Telecom サブシステムが着信の拒否を求める場合に、Telecom サブシステムによって呼び出されます。アプリは通話を拒否した後、setDisconnected(DisconnectCause) を呼び出して REJECTED をパラメータとして指定する必要があります。そのうえで、アプリは destroy() メソッドを呼び出して、アプリが通話を処理済みであることをシステムに通知する必要があります。Telecom サブシステムは、ユーザーがアプリで着信を拒否した場合にこのメソッドを呼び出します。

onDisconnect()

このメソッドは、Telecom サブシステムが通話の切断を求める場合に、Telecom サブシステムによって呼び出されます。通話が終了したら、アプリは setDisconnected(DisconnectCause) メソッドを呼び出し、ユーザー リクエストによって通話が切断されたことを示すために、パラメータとして LOCAL を指定する必要があります。そのうえで、アプリは destroy() メソッドを呼び出して、アプリが通話を処理済みであることを Telecom サブシステムに通知する必要があります。このメソッドは、ユーザーが Android Auto などの別の通話サービスを介して通話を切断した場合に、システムによって呼び出されることがあります。また、現在の通話を切断して他の通話を発信できるようにする必要がある場合(ユーザーが緊急通報を発信する必要がある場合など)にも、システムによってこのメソッドが呼び出されることがあります。通話サービスについて詳しくは、InCallService をご覧ください。

一般的な通話シナリオを処理する

通話フローでの ConnectionService API の使用には、android.telecom パッケージ内の他のクラスとのやり取りが含まれます。以降のセクションでは、一般的な通話シナリオと、アプリで同 API を使用してそうした通話を処理する方法について説明します。

着信に応答する

着信の処理フローは、他のアプリに進行中の通話があるかどうかによって変わります。処理フローが変わる理由は、デバイス上のすべての通話アプリに安定した環境を確実に提供することを目的として、他のアプリにアクティブな通話がある場合に Telecom フレームワークがいくつかの制約を設ける必要があるためです。詳しくは、通話の制約をご覧ください。

他のアプリにアクティブな通話がない場合

他のアプリでアクティブな通話がない場合は、次の手順に沿って着信に応答します。

  1. アプリが通常のメカニズムを使用して新しい着信を受け取ります。
  2. addNewIncomingCall(PhoneAccountHandle, Bundle) メソッドを使用して、新しい着信を Telecom サブシステムに通知します。
  3. Telecom サブシステムがアプリの ConnectionService の実装にバインドし、onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) メソッドを使用して、新しい着信を表す Connection クラスの新しいインスタンスをリクエストします。
  4. Telecom サブシステムが onShowIncomingCallUi() メソッドを使用して、アプリに対し、自身の着信ユーザー インターフェースを表示する必要があることを通知します。
  5. アプリが、関連する全画面インテントを持つ通知を使用して着信 UI を表示します。詳しくは onShowIncomingCallUi() をご覧ください。
  6. ユーザーが着信に応答した場合は setActive() メソッドを呼び出します。ユーザーが着信を拒否した場合は、パラメータに REJECTED を指定した setDisconnected(DisconnectCause) メソッドを呼び出した後、destroy() メソッドを呼び出します。

他のアプリに、保留にできないアクティブな通話がある場合

他のアプリに保留にできないアクティブな通話がある場合は、次の手順に沿って着信に応答します。

  1. アプリが通常のメカニズムを使用して新しい着信を受け取ります。
  2. addNewIncomingCall(PhoneAccountHandle, Bundle) メソッドを使用して、新しい着信を Telecom サブシステムに通知します。
  3. Telecom サブシステムがアプリの ConnectionService の実装にバインドし、onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) メソッドを使用して、新しい着信を表す Connection オブジェクトの新しいインスタンスをリクエストします。
  4. Telecom サブシステムが、アプリへの着信を示す着信 UI を表示します。
  5. ユーザーが着信に応答した場合、Telecom サブシステムは onAnswer() メソッドを呼び出します。アプリ側では setActive() メソッドを呼び出して、通話に接続したことを Telecom サブシステムに通知する必要があります。
  6. ユーザーが通話を拒否した場合、Telecom サブシステムは onReject() メソッドを呼び出します。アプリ側ではパラメータに REJECTED を指定して setDisconnected(DisconnectCause) メソッドを呼び出した後、destroy() メソッドを呼び出す必要があります。

通話を発信する

通話の発信フローには、Telecom フレームワークが設けた制約により通話を発信できない場合の処理が含まれます。詳しくは、通話の制約をご覧ください。

通話を発信するには、次の手順に従います。

  1. ユーザーがアプリ内で発信を開始します。
  2. placeCall(Uri, Bundle) メソッドを使用して、新しい発信を Telecom サブシステムに通知します。メソッドのパラメータについての考慮事項を以下に示します。
    • Uri パラメータは、通話を発信している先のアドレスを表します。通常の電話番号の場合は、tel: URI スキームを使用します。
    • Bundle パラメータを使用してアプリの PhoneAccountHandle オブジェクトを EXTRA_PHONE_ACCOUNT_HANDLE エクストラに追加することで、通話アプリについての情報を提供できます。アプリはすべての発信に PhoneAccountHandle オブジェクトを提供する必要があります。
    • また、Bundle パラメータを使用して、EXTRA_START_CALL_WITH_VIDEO_STATE エクストラに STATE_BIDIRECTIONAL の値を指定することで、発信が動画を含んでいるかどうかを指定できます。デフォルトでは、Telecom サブシステムはビデオ通話をスピーカーフォンにルーティングします。
  3. Telecom サブシステムがアプリの ConnectionService の実装にバインドします。
  4. アプリからの発信ができない場合、Telecom サブシステムは onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) メソッドを呼び出して、現在通話を開始できないことをアプリに通知します。アプリ側では、通話を開始できないことをユーザーに通知する必要があります。
  5. アプリからの発信が可能な場合、Telecom サブシステムは onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest) メソッドを呼び出します。アプリ側では、新しい発信を表す Connection クラスのインスタンスを返す必要があります。接続で設定する必要のあるプロパティについて詳しくは、接続サービスを実装するをご覧ください。
  6. 発信が接続されたら setActive() メソッドを呼び出して、通話がアクティブであることを Telecom サブシステムに通知します。

通話を終了する

通話を終了するには、次の手順に従います。

  1. setDisconnected(DisconnectCause) を呼び出します。その際、ユーザーが通話を終了した場合はパラメータで LOCAL を送信し、相手が通話を終了した場合はパラメータで REMOTE を送信します。
  2. destroy() メソッドを呼び出します。

通話の制約

一貫性のあるシンプルな通話環境をユーザーに確実に提供するために、Telecom フレームワークはデバイス上の通話の管理にいくつかの制約を課しています。たとえば、ユーザーが FooTalk と BarTalk という 2 つの通話アプリをインストールしていて、それぞれのアプリが自己管理 ConnectionService API を実装しているとします。この場合、次の制約が適用されます。

  • API レベル 27 以前を搭載したデバイスでは、一度に 1 つのアプリのみが進行中の通話を保持できます。この制約により、ユーザーが FooTalk アプリを使用して通話中の場合、BarTalk アプリでは新しい通話を発着信することができません。

    API レベル 28 以降を搭載したデバイスでは、FooTalk と BarTalk の両方が CAPABILITY_SUPPORT_HOLD 権限と CAPABILITY_HOLD 権限を宣言していれば、ユーザーはアプリを切り替えて別の通話を開始または通話に応答することで、進行中の通話を複数保持できます。

  • ユーザーが(内蔵の電話アプリを使用するなどして)通常管理の通話を進行中である場合、ユーザーは通話アプリを介した通話を行うことはできません。つまり、ユーザーが携帯通信会社を使用して通常の通話をしている場合、そのユーザーは FooTalk の通話も BarTalk の通話も同時にすることはできません。

  • ユーザーが緊急通報を発信すると、Telecom サブシステムはアプリの通話を切断します。

  • ユーザーが緊急通報をしている間、アプリでの電話の発着信はできません。

  • アプリが着信を受けたときに別の通話アプリで進行中の通話がある場合、着信に応答すると、他のアプリで進行中の通話は終了します。アプリは自身の通常の着信ユーザー インターフェースを表示してはなりません。Telecom フレームワークが着信ユーザー インターフェースを表示し、ユーザーに対して、新しい通話に応答すると進行中の通話が終了することを伝えます。つまり、ユーザーが FooTalk での通話中に BarTalk アプリで通話が着信した場合、Telecom フレームワークはユーザーに対して、BarTalk に新しい通話が着信していること、BarTalk の通話に応答すると FooTalk の通話が終了することを伝えます。