I need to detect when calls come to the phone and get the name (what is shown when the call comes in, even if it is just a number or SPAM), number and photo.
I tried to do so, in the end I had the call detected only after acceptance and never had photo uri. I also tried this, but it doesn't work at all.
I want to find a not deprecated way to do this and also it should work on new android versions even if the phone is in silent mode (this is important, I've tried this solution, but it doesn't work without a sound)
I will be grateful for any help
This works for me:
class CustomPhoneStateReceiver(private val onResult: (String, String?, Uri?) -> Unit) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
println("CustomPhoneStateReceiver onReceive")
val state = intent?.getStringExtra(TelephonyManager.EXTRA_STATE)
val incomingNumber = intent?.getStringExtra("incoming_number")
if (state == TelephonyManager.EXTRA_STATE_RINGING || state == TelephonyManager.EXTRA_STATE_OFFHOOK) {
println("TelephonyManager.CALL_STATE_RINGING onReceive -> $incomingNumber")
incomingNumber?.let { number ->
val (name, photoUri) = getCallerInfo(context, number)
onResult(number, name, photoUri)
}
}
}
private fun getCallerInfo(context: Context, phoneNumber: String): Pair<String?, Uri?> {
val uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber))
val projection = arrayOf(ContactsContract.PhoneLookup.DISPLAY_NAME, ContactsContract.PhoneLookup.PHOTO_URI)
context.contentResolver.query(uri, projection, null, null, null).use { cursor ->
if (cursor?.moveToFirst() == true) {
val nameIndex = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME)
val photoIndex = cursor.getColumnIndex(ContactsContract.PhoneLookup.PHOTO_URI)
val name = cursor.getString(nameIndex)
val photoUri = cursor.getString(photoIndex)?.let { Uri.parse(it) }
return Pair(name, photoUri)
}
}
val cancellationSignal = CancellationSignal()
val cursor = context.contentResolver.query(
CallLog.Calls.CONTENT_URI,
null,
CallLog.Calls.TYPE + " = ?",
arrayOf(CallLog.Calls.INCOMING_TYPE.toString()),
CallLog.Calls.DATE + " DESC",
cancellationSignal
)
if(cursor != null && cursor.moveToFirst()) {
val indexName = cursor.getColumnIndex(CallLog.Calls.CACHED_NAME)
val name = cursor.getString(if (indexName < 0) 0 else indexName)
println(indexName)
val indexUri = cursor.getColumnIndex(CallLog.Calls.CACHED_PHOTO_URI)
val photoUri = cursor.getString(if (indexUri < 0) 0 else indexUri)?.let { Uri.parse(it) }
cancellationSignal.cancel()
cursor.close()
return Pair(name, photoUri)
}
return Pair(null, null)
}
}
And in MainActivity.kt:
// ...
private val onResult: (String, String?, Uri?) -> Unit = { phone, name, photoUri ->
println("onResult -> $phone, $name, $photoUri ")
val dataTv = findViewById<TextView>(R.id.data_tv)
val imageView = findViewById<ImageView>(R.id.imageView_screenshot)
val uriString = photoUri.toString().ifBlank { "No photo" }
val formattedName = name?.ifBlank { phone } ?: phone
dataTv.text = "phone number: $phone\ncallerName: $formattedName\nimageUri: ${if(uriString == "null") "No photo" else uriString}"
requestMediaProjection()
}
private val receiver = CustomPhoneStateReceiver(onResult)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
registerReceiver(receiver, IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED))
// ...
}