This project shows the absolute minimum an Android Phone app needs to implement to replace the native phone app and provide the UI when making calls. It was created to address the lack of good documentation, evidenced by multiple questions on stackoverflow (1), (2).
This app uses minSdkVersion 23
, because that's when the APIs supporting this were added.
There are two steps an app has to make to show its own UI during an ongoing call. One is to implement an InCallService
that Android will use to notify you about events related to the calls. But before the system will let you know about any call, the user must first choose your app as the default Phone app, and you need to make it available to him as such.
To have your app listed as a Phone app, you must have an activity with at least those intent filters (to handle both cases mentioned in documentation of ACTION_DIAL
, also mentioned in DefaultDialerManager
hidden class):
<intent-filter>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
</intent-filter>
And to be honest, that's a bit counterintuitive, because setting the default Phone app is separate from setting a default Dialer – the former controls only the ongoing call UI, while the latter controls only the dialing UI.
Filters in the AndroidManifest improve a bit over that minimum, to allow setting the app as the default Dialer, and launching dialer from web browser. The Dialer app in AOSP has even more filters declared.
Anyway, this will make your app available in the Settings
-> Apps & notifications
-> Advanced
-> Default apps
-> Phone app
:
You can make it easier for the user to set your app as the default Phone app with the help from TelecomManager
:
if (getSystemService(TelecomManager::class.java).defaultDialerPackage != packageName) {
Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName)
.let(::startActivity)
}
This will show a dialog similar to this:
You need to define an InCallService
implementation the system will bind to and notify you about the call:
<service
android:name=".CallService"
android:permission="android.permission.BIND_INCALL_SERVICE">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true" />
<intent-filter>
<action android:name="android.telecom.InCallService" />
</intent-filter>
</service>
There you should handle at least onCallAdded
(set up listeners on Call
, start your UI - activity - for the call) and onCallRemoved
(remove listeners), like CallService
does in a simplified way.
If the user wants to answer the call, you need to invoke the method Call#answer(int)
(with VideoProfile.STATE_AUDIO_ONLY
for example). In this example CallActivity
reacts to user input by calling those methods on Call
object shared through the OngoingCall
singleton.
Check out Call.Callback
for events that can happen with a single call. This sample uses just the onStateChanged
callback, to update the UI and finish the activity when the remote party hangs up.