I'm developing an Android app that needs to track the outgoing call phone number and execute a deeplink to a specific screen in my app. However, I cannot ask users to use my app for making calls instead of using the default dialer.
Starting from Android 10, the PROCESS_OUTGOING_CALLS permission has been deprecated, and I can't find a way to get the phone number of outgoing calls without creating my own dialing interface. Is there any workaround to get the phone number of outgoing calls on Android 10 without creating a custom dialer interface?
I tried using a BroadcastReceiver to listen for the NEW_OUTGOING_CALL action, but the EXTRA_PHONE_NUMBER field is empty, and I cannot get the phone number of the outgoing call. I expected to obtain the phone number in the BroadcastReceiver and then pass it to a foreground service that would handle the deeplink.
How get the phone number if is coming up empty inside onCallStateChanged?
Below I will share my code:
- Manifest permissions:
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
- OutgoingCallReceiver:
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
class OutgoingCallReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_NEW_OUTGOING_CALL) {
val phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)
if (!phoneNumber.isNullOrEmpty()) {
// Start the service for intercepting outgoing calls
val serviceIntent = Intent(context, OutgoingCallService::class.java)
ContextCompat.startForegroundService(context, serviceIntent)
}
}
}
}
- OutgoingCallService:
import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.IBinder
import android.telephony.PhoneStateListener
import android.telephony.TelephonyManager
class OutgoingCallService : Service() {
private lateinit var telephonyManager: TelephonyManager
private lateinit var phoneStateListener: PhoneStateListener
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
phoneStateListener = createPhoneStateListener()
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
}
@SuppressLint("MissingPermission")
private fun createPhoneStateListener(): PhoneStateListener {
return object : PhoneStateListener() {
override fun onCallStateChanged(state: Int, phoneNumber: String?) {
super.onCallStateChanged(state, phoneNumber)
when (state) {
TelephonyManager.CALL_STATE_OFFHOOK -> {
if (phoneNumber == "SPECIFIC PHONE NUMBER") {
redirectToApp()
}
}
}
}
}
}
private fun redirectToApp() {
// Redirect application's specific screen using an Intent
val context = applicationContext
val deepLinkIntent = Intent(Intent.ACTION_VIEW, Uri.parse("scheme://host"))
deepLinkIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(deepLinkIntent)
}
}
- MainActivity:
import android.Manifest
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.callinterceptor.ui.theme.CallInterceptorTheme
class MainActivity : ComponentActivity() {
private val outgoingCallReceiver = OutgoingCallReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Check and request permissions if necessary
checkAndRequestPermissions()
// Register BroadcastReceiver to intercept outgoing calls
registerOutgoingCallReceiver()
}
private fun checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val permissions = arrayOf(
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.PROCESS_OUTGOING_CALLS
)
val missingPermissions = ArrayList<String>()
for (permission in permissions) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
missingPermissions.add(permission)
}
}
if (missingPermissions.isNotEmpty()) {
ActivityCompat.requestPermissions(
this,
missingPermissions.toTypedArray(),
PERMISSIONS_REQUEST_CODE
)
}
}
}
private fun registerOutgoingCallReceiver() {
val intentFilter = IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL)
registerReceiver(outgoingCallReceiver, intentFilter)
}
private fun unregisterOutgoingCallReceiver() {
unregisterReceiver(outgoingCallReceiver)
}
companion object {
private const val PERMISSIONS_REQUEST_CODE = 123
}
override fun onDestroy() {
super.onDestroy()
// Unregister the BroadcastReceiver
unregisterOutgoingCallReceiver()
}
}