打造通話應用程式

通話應用程式可讓使用者在裝置上接收或撥打語音/視訊通話。來電應用程式會使用專屬的通話使用者介面進行通話,而非使用預設的「電話」應用程式介面,如以下螢幕截圖所示。

通話應用程式範例
使用自有使用者介面的通話應用程式範例

Android 架構包含 android.telecom 套件,其中包含的類別,可協助您根據電信架構建構呼叫應用程式。根據電信架構建構應用程式可提供下列好處:

  • 您的應用程式能與裝置原生電信子系統正確互通。
  • 您的應用程式與其他遵守架構的呼叫應用程式可正確互通。
  • 這個架構可協助應用程式管理音訊和影片轉送。
  • 架構可協助應用程式判斷呼叫是否聚焦。

資訊清單宣告和權限

在應用程式資訊清單中,宣告應用程式使用 MANAGE_OWN_CALLS 權限,如以下範例所示:

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

如要進一步瞭解如何宣告應用程式權限,請參閱權限

您必須宣告指定在應用程式中實作 ConnectionService 類別的服務。電信子系統必須宣告 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>

如要進一步瞭解如何宣告應用程式元件 (包括服務),請參閱「應用程式元件」。

導入連線服務

呼叫應用程式必須提供 ConnectionService 類別的實作,該類別可供電信子系統繫結。ConnectionService 實作應覆寫下列方法:

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

電信子系統會呼叫此方法,以回應應用程式呼叫 placeCall(Uri, Bundle) 來建立新的撥出呼叫。應用程式會傳回 Connection 類別實作的新例項 (詳情請參閱「實作連線」) 以代表新的傳出呼叫。您可以執行下列動作,進一步自訂外送連線:

onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

當應用程式呼叫 placeCall(Uri, Bundle) 方法且無法撥出電話時,電信子系統會呼叫此方法。為因應這種情況,您的應用程式應通知使用者 (例如使用快訊方塊或浮動式訊息),說明無法發起撥出呼叫。如果有進行中的緊急通話,或是有其他應用程式正在進行的通話,因此無法在撥打電話前通話,應用程式就可能無法撥打電話。

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

當應用程式呼叫 addNewIncomingCall(PhoneAccountHandle, Bundle) 方法以告知系統有新的來電時,電信子系統會呼叫此方法。應用程式會傳回 Connection 實作的新執行個體 (詳情請參閱「實作連線」以代表新來電)。您可以執行下列動作,進一步自訂連入連線:

onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

應用程式呼叫 addNewIncomingCall(PhoneAccountHandle, Bundle) 方法以通知電信有新來電時,電信子系統會呼叫此方法,但不允許來電 (詳情請參閱「呼叫限制」)。應用程式應無聲拒絕來電,可選擇張貼通知,通知使用者已接來電。

實作連線

您的應用程式應該建立 Connection 的子類別,代表應用程式中的呼叫。您應該在實作中覆寫下列方法:

onShowIncomingCallUi()

當您新增來電時,電信子系統會呼叫此方法,應用程式應顯示來電 UI。

onCallAudioStateChanged(CallAudioState)

電信子系統會呼叫此方法,告知應用程式目前的音訊路徑或模式已變更。為了回應應用程式使用 setAudioRoute(int) 方法變更音訊模式,系統會呼叫此方法。如果系統變更音訊路徑 (例如藍牙耳機中斷連線時),也可以呼叫此方法。

onHold()

如果電信子系統想要保留通話,就會呼叫此方法。為回應這項要求,應用程式應保留呼叫,然後叫用 setOnHold() 方法,通知系統保留呼叫。當顯示通話的服務 (例如 Android Auto) 顯示通話時,電信子系統可能會呼叫這個方法。如果使用者在其他應用程式中進行通話,電信子系統也會呼叫這個方法。如要進一步瞭解通話服務,請參閱 InCallService

onUnhold()

當電信子系統想要恢復處於保留狀態的呼叫時,會呼叫此方法。應用程式恢復呼叫後,應叫用 setActive() 方法,通知系統呼叫不再處於保留狀態。當顯示通話的服務 (例如 Android Auto) 顯示通話希望轉發恢復通話時,電信子系統可能會呼叫這個方法。如要進一步瞭解通話服務,請參閱 InCallService

onAnswer()

電信子系統會呼叫此方法,告知應用程式應接聽來電。應用程式接聽呼叫後,應叫用 setActive() 方法,通知系統已接聽呼叫。當應用程式新增來電,且已在其他應用程式中進行通話且無法保留時,電信子系統可能會呼叫此方法。在這類執行個體中,電信子系統會代表您的應用程式顯示來電 UI。該架構提供超載方法,可支援指定接聽來電時的影片狀態。詳情請參閱 onAnswer(int)

onReject()

當電信子系統想要拒接來電時,會呼叫此方法。應用程式拒絕呼叫後,應呼叫 setDisconnected(DisconnectCause) 並指定 REJECTED 做為參數。接著,應用程式應呼叫 destroy() 方法,通知系統應用程式已處理呼叫。當使用者拒絕來自應用程式的來電時,電信子系統會呼叫此方法。

onDisconnect()

當電信子系統想要中斷通話時,會呼叫這個方法。呼叫結束後,應用程式應呼叫 setDisconnected(DisconnectCause) 方法,並指定 LOCAL 做為參數,表示使用者要求導致呼叫中斷。接著,應用程式應呼叫 destroy() 方法,向電信子系統通知應用程式已處理呼叫。當使用者透過其他通話服務 (例如 Android Auto) 中斷通話時,系統可能會呼叫這個方法。如果使用者想撥打緊急電話,則必須中斷通話才能撥打其他電話時,系統也會呼叫此方法。如要進一步瞭解通話服務,請參閱 InCallService

處理常見的呼叫情境

在呼叫流程中使用 ConnectionService API 涉及與 android.telecom 套件中的其他類別互動。以下各節說明常見的呼叫情境,以及應用程式應如何使用 API 來處理這些情境。

接聽來電

處理來電的流程會隨其他應用程式呼叫,而改變。造成資料流的差異的原因是,當其他應用程式在其他應用程式中進行呼叫時,電信架構必須建立一些限制,確保裝置上所有呼叫的應用程式都能穩定使用。詳情請參閱「呼叫限制」。

其他應用程式中沒有進行中的通話

如要在其他應用程式中沒有任何進行中的通話時接聽來電,請按照下列步驟操作:

  1. 應用程式會使用一般機制接收新的來電。
  2. 請使用 addNewIncomingCall(PhoneAccountHandle, Bundle) 方法,通知電信子系統有新的來電。
  3. 電信子系統繫結至應用程式的 ConnectionService 實作,並使用 onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) 方法要求代表新來電的 Connection 類別新例項。
  4. 電信子系統會通知應用程式,使用 onShowIncomingCallUi() 方法顯示來電使用者介面。
  5. 應用程式會使用具有相關聯全螢幕意圖的通知,顯示傳入的 UI。詳情請參閱 onShowIncomingCallUi()
  6. 如果使用者接受來電,請呼叫 setActive() 方法;如果使用者拒絕來電,請呼叫 setDisconnected(DisconnectCause) 指定 REJECTED 參數,接著再呼叫 destroy() 方法。

其他無法設為保留狀態的應用程式正在進行的通話

如要在其他應用程式中有無法保留的通話時接聽來電,請按照下列步驟操作:

  1. 應用程式會使用一般機制接收新的來電。
  2. 請使用 addNewIncomingCall(PhoneAccountHandle, Bundle) 方法,通知電信子系統有新的來電。
  3. 電信子系統繫結至應用程式的 ConnectionService 實作,並使用 onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) 方法要求代表新來電的 Connection 物件執行個體。
  4. 電信子系統會顯示來電的來電 UI。
  5. 如果使用者接受呼叫,電信子系統會呼叫 onAnswer() 方法。您應該呼叫 setActive() 方法,向電信子系統表示呼叫現已連線。
  6. 如果使用者拒絕通話,電信子系統會呼叫 onReject() 方法。您應該呼叫 setDisconnected(DisconnectCause) 方法,將 REJECTED 指定為參數,然後呼叫 destroy() 方法。

撥出電話

撥打電話的流程涉及處理因電信架構設定限製而無法放置呼叫的可能性。詳情請參閱「通話限制」。

如要撥打電話,請按照下列步驟操作:

  1. 使用者在您的應用程式中撥出電話。
  2. 請使用 placeCall(Uri, Bundle) 方法,通知電信子系統有關新的撥出呼叫。請考量下列方法參數的注意事項:
    • Uri 參數代表呼叫傳送目標的位址。如果是一般電話號碼,請使用 tel: URI 配置。
    • 透過 Bundle 參數,您可以將應用程式的 PhoneAccountHandle 物件新增至 EXTRA_PHONE_ACCOUNT_HANDLE 額外項目,以提供呼叫應用程式的相關資訊。您的應用程式必須為每個撥出呼叫提供 PhoneAccountHandle 物件。
    • 您也可以透過 Bundle 參數,在 EXTRA_START_CALL_WITH_VIDEO_STATE 額外項目中指定 STATE_BIDIRECTIONAL 值,藉此指定傳出呼叫是否包含視訊。根據預設,電信子系統會將視訊通話轉送至喇叭。
  3. 電信子系統會繫結至應用程式的 ConnectionService 實作。
  4. 如果應用程式無法撥出電話,電信子系統會呼叫 onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) 方法,告知應用程式目前無法撥打該呼叫。應用程式應通知使用者無法撥打電話。
  5. 如果應用程式可以撥出電話,電信子系統會呼叫 onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest) 方法。應用程式應傳回 Connection 類別的執行個體,代表新的撥出呼叫。如要進一步瞭解您應在連線中設定的屬性,請參閱實作連線服務
  6. 撥出電話後,請呼叫 setActive() 方法,向電信子系統通知通話正在進行中。

結束通話

如要結束通話,請按照下列步驟操作:

  1. 如果使用者已終止呼叫,請呼叫 setDisconnected(DisconnectCause) 以做為參數,並將 LOCAL 做為參數;如果其他方已終止呼叫,則請傳送 REMOTE 做為參數。
  2. 呼叫 destroy() 方法。

通話限制

為確保使用者享有一致且簡單的通話體驗,電信架構會針對管理裝置上的呼叫強制執行一些限制。舉例來說,假設使用者安裝了兩個呼叫應用程式,這兩個應用程式實作自行管理的 ConnectionService API、FooTalk 和 BardTalk。在這種情況下,系統會套用以下限制:

  • 在搭載 API 級別 27 或以下級別的裝置上,只有一個應用程式可以隨時保持進行中的呼叫。這項限制意味著,當使用者使用 FooTalk 應用程式進行通話時,BarTalk 應用程式無法啟動或接收新的呼叫。

    在搭載 API 級別 28 以上版本的裝置上,如果 FooTalk 和 BarTalk 宣告了 CAPABILITY_SUPPORT_HOLDCAPABILITY_HOLD 權限,使用者可以切換應用程式來啟動或接聽其他呼叫,維持多個進行中的呼叫。

  • 如果使用者參與的是一般管理的通話 (例如使用內建的電話或撥號應用程式),則使用者無法透過通話應用程式加入通話。也就是說,如果使用者正在使用行動電信業者進行一般通話,就無法同時進行 FooTalk 或 BarTalk 通話。

  • 如果使用者撥打緊急電話,電信子系統會中斷應用程式的通話連線。

  • 當使用者正在緊急電話時,應用程式無法接聽或撥打電話。

  • 如果應用程式收到來電時,在另一個呼叫應用程式中有進行中的通話,則接聽來電後,系統會在其他應用程式中結束所有進行中的通話。應用程式不應顯示平常的來電使用者介面。電信架構會顯示來電使用者介面,並通知使用者接聽新通話將結束目前的通話。這表示如果使用者正在進行 FooTalk 通話,且 BarTalk 應用程式接到來電,則電信架構會通知使用者有新的來電,通知 BarTalk 通話會結束。