0

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()
    }
}
frederyk
  • 1
  • 1

1 Answers1

0

I managed to solve this problem by implementing a simple BroadcastReceiver (the code is huge summarized)!

internal class PhoneStateReceiver : BroadcastReceiver() {

    private var filters: IntentFilter = IntentFilter()

    init {
        filters.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED)
        filters.addAction(Intent.ACTION_NEW_OUTGOING_CALL)
    }

    fun register(context: Context) = context.registerReceiver(this, filters)

    fun unregister(context: Context) = context.unregisterReceiver(this)

    override fun onReceive(context: Context, intent: Intent) {
        var phoneNumer = ""
        val a = intent.action
        if (a == TelephonyManager.ACTION_PHONE_STATE_CHANGED) {
            phoneNumer = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER) ?: ""
        }
        if (a == Intent.ACTION_NEW_OUTGOING_CALL) {
            phoneNumer = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER) ?: ""
        } 
    }
}
frederyk
  • 1
  • 1