/simple-phone

Bare minimum implementation of an Android Phone app

Primary LanguageKotlinMIT LicenseMIT

Simple Phone

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.

Becoming a default Phone app

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:

settings

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:

change default dialer dialog

Handling calls

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.

call