TelephonyManager deprecated listen() CALL_STATE_RINGING on android 12
Asked Answered
C

3

14

I'd like to listen if there's a phone call happening while my app is in the foreground.

It was like this before but now listen() is deprecated:

val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
            tm.listen(object : PhoneStateListener() {
                override fun onCallStateChanged(state: Int, phoneNumber: String?) {
                    super.onCallStateChanged(state, phoneNumber)
                    when (state) {
                        TelephonyManager.CALL_STATE_RINGING -> transcribingAudioConsumer.stopTranscription(null)
                        else -> {}
                    }
                }
            }, PhoneStateListener.LISTEN_CALL_STATE)

I tried something like this but I couldn't find the correct way to implement it.

         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                        tm.registerTelephonyCallback({ it.run() }, MyPhoneStateListener())
                    }
  @RequiresApi(Build.VERSION_CODES.S)
    class MyPhoneStateListener : TelephonyCallback(), TelephonyCallback.CallStateListener {
        override fun onCallStateChanged(state: Int) {
            when (state) {
                TelephonyManager.CALL_STATE_RINGING -> {
                    Timber.e("omg RING")
                }

                TelephonyManager.CALL_STATE_OFFHOOK -> {
                    Timber.e("omg hook")
                }
                TelephonyManager.CALL_STATE_IDLE -> {
                    Timber.e("omg idle")
                }
            }
        }
    }
Cerebrovascular answered 14/10, 2021 at 12:49 Comment(0)
C
15

Since the listen method is deprecated since api 31 android 12 I made a simple way to listen to the telephony callbacks.

val telephonyManager: TelephonyManager =
    context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    telephonyManager.registerTelephonyCallback(
        context.mainExecutor,
        object : TelephonyCallback(), TelephonyCallback.CallStateListener {
            override fun onCallStateChanged(state: Int) {
            }
        })
} else {
    telephonyManager.listen(object : PhoneStateListener() {
        override fun onCallStateChanged(state: Int, phoneNumber: String?) {
        }
    }, PhoneStateListener.LISTEN_CALL_STATE)
}

Note that the new callback does not include a phone number. At least for broadcast receiver the phone number can be retrieved via

intent.extras.getString("incoming_number")
Cobden answered 10/2, 2022 at 9:35 Comment(6)
after adding, i am receiving security exception in Android 12, Do we need to add any permissions in Android 12?Parathion
@RaviGadipudi, you need to check READ_PHONE_STATE permission private fun isReadPhoneStateGranted() = ActivityCompat.checkSelfPermission(this.context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTEDCerebrovascular
Yes, Found it thru behavioural changes of Android 12 document. Thanks for the reply. @OrcunSevsayParathion
can anyone convert this code to java? This is because i do not understand kotlin. Thanks in advance.Joacima
I want this code in Java, please someone share it ASAPAutocatalysis
@Cobden How can I get phone number with this new callback? Is there any way?Immix
N
4

ServiceReceiver(Java)

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;

public class ServiceReceiver extends BroadcastReceiver {

@RequiresApi(api = Build.VERSION_CODES.S)
@Override
public void onReceive(Context context, Intent intent) {
    registerCustomTelephonyCallback(context);
}

@RequiresApi(Build.VERSION_CODES.S)
class CustomTelephonyCallback extends TelephonyCallback implements TelephonyCallback.CallStateListener {
    private CallBack mCallBack;

    public CustomTelephonyCallback(CallBack callBack) {
        mCallBack = callBack;
    }

    @Override
    public void onCallStateChanged(int state) {

        mCallBack.callStateChanged(state);

    }
}


@RequiresApi(Build.VERSION_CODES.S)
public void registerCustomTelephonyCallback(Context context) {
    TelephonyManager telephony = (TelephonyManager) context
            .getSystemService(Context.TELEPHONY_SERVICE);

    telephony.registerTelephonyCallback(context.getMainExecutor(), new CustomTelephonyCallback(new CallBack() {
        @Override
        public void callStateChanged(int state) {

            int myState=state;

        }
    }));


}

interface CallBack {
    void callStateChanged(int state);
}

}

Nupercaine answered 12/11, 2021 at 14:14 Comment(4)
Thanks but onReceive never called.I am testing it on android emulator.Longmire
The written code only works on android 12. Make sure your device is android 12.Biagio
it works, thank you! But test on physical device, using emulator appear an error!Conciliar
I am not able to get onRecieve on my android 12 device, could someone please share the full code in java - starting from creating a ServiceRequestor object. Thanks.Malaysia
N
2

I used what you did and android 12 emulator also worked.I used this for versions less than android 12 I hope it works.

    class MyPhoneStateListener() : PhoneStateListener() {
    override fun onCallStateChanged(state: Int, incomingNumber: String) {
        when (state) {
            TelephonyManager.CALL_STATE_IDLE -> {
                Log.e("MyPhoneStateListener", "IDLE")
            
            }
            TelephonyManager.CALL_STATE_OFFHOOK -> {
                Log.e("MyPhoneStateListener", "OFFHOOK")
            }
            TelephonyManager.CALL_STATE_RINGING -> {
                Log.e("MyPhoneStateListener", "RINGING")
            }
        }
    }


}

I used this to listen to the call


 phoneListener = MyPhoneStateListener()
             var telephony: TelephonyManager? = null
                telephony = context
                    .getSystemService(TELEPHONY_SERVICE) as TelephonyManager?

                telephony!!.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE)

finally you should make a ServiceReceiver class like here. Add PhoneStateListener

Android 12 :I found an example here. I hope it works for you. https://note.com/koh_49/n/n94a5c2ae3aa2

  @RequiresApi(Build.VERSION_CODES.S)
class CustomTelephonyCallback(private val func: (state: Int) -> Unit) :
TelephonyCallback(),
TelephonyCallback.CallStateListener {
    override fun onCallStateChanged(state: Int) {
        func(state)
    }
}
@RequiresApi(Build.VERSION_CODES.S)
fun registerCustomTelephonyCallback(){

     var callback: CustomTelephonyCallback? = null


        (getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager)?.
        registerTelephonyCallback(
            mainExecutor,
            CustomTelephonyCallback {
                //
                var state=it
             

            }.also {
                callback = it
            }
        )


}

  

Manifest

    <receiver android:name=".data.service.receiver.ServiceReceiver"
        android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
    </receiver>

ServiceReceiver

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getMainExecutor


class ServiceReceiver: BroadcastReceiver() {
@RequiresApi(Build.VERSION_CODES.S)
override fun onReceive(context: Context?, intent: Intent?) {

    registerCustomTelephonyCallback(context)


}

@RequiresApi(Build.VERSION_CODES.S)
class CustomTelephonyCallback(private val func: (state: Int) -> Unit) :
    TelephonyCallback(),
    TelephonyCallback.CallStateListener {
    override fun onCallStateChanged(state: Int) {
        func(state)
    }
}

@RequiresApi(Build.VERSION_CODES.S)
fun registerCustomTelephonyCallback(context: Context?){

    var callback: CustomTelephonyCallback? = null

    (context?.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager)?.
    registerTelephonyCallback(
        getMainExecutor(context),
        CustomTelephonyCallback {
            //
            var state=it


        }.also {
            callback = it
        }
    )


}

}

Nupercaine answered 8/11, 2021 at 15:40 Comment(15)
telephony.listen() and PhoneStateListener() are deprecated. I know it's still working on Android 12, but I'd like to integrate with registerTelephonyCallback(Executor, TelephonyCallback).Cerebrovascular
Deprecated Use registerTelephonyCallback(Executor, TelephonyCallback). Params: listener – The PhoneStateListener object to register (or unregister) events – The telephony state(s) of interest to the listener, as a bitwise-OR combination of PhoneStateListener LISTEN_ flags. You may see Deprecated and suggestion message under TelephonyManager.java listen(..) classCerebrovascular
Thank you for your answer.yes I saw it deprecated "telephony.listen()" while using it in my project, then I started researching. I found your question and solution. This use "registerTelephonyCallback(Executor, TelephonyCallback" is not correct?. I m use your solution now for android 12Biagio
I'm trying to understand how to build it with TelephonyCallback instead of telephony.listen() and PhoneStateListener. If you can help me to find how to integrate with TelephonyCallback, I'll be appreciatedCerebrovascular
What's mainExecutor that you've shared? @Enes BAŞKAYACerebrovascular
ı m use "mainExecutor" in my class. class MyCallScreeningService : CallScreeningService() . I call it from within this class of android. package android.content; .... /** * Proxying implementation of Context that simply delegates all of its calls to * another Context. Can be subclassed to modify behavior without changing * the original Context. */ public class ContextWrapper extends Context { .... @Override public Executor getMainExecutor() { return mBase.getMainExecutor(); }Biagio
When I import it in my receiver class, it imports from this package."import androidx.core.content.ContextCompat.getMainExecutor"Biagio
Could you also share your Receiver class and how you call it in Manifest, please?Cerebrovascular
I updated my answer.There was no receiver class in my project. I wrote this and tried it now it works.@Orcun SevsayBiagio
Greate Answer. Thanks a lot! It works pretty good!Cerebrovascular
You are welcome.I'm glad it was useful. I wish you good work.Biagio
@EnesBAŞKAYA Can you provide the same snippet for JAVA?Longmire
I shared an example for Java. Hope it helps you @LongmireBiagio
@EnesBAŞKAYA Thanks a lot mate. Can we use this like registering inside activity instead of manifest?Longmire
@Longmire You're welcome .I've never done this before but you can send the data in the receiver class to the relevant activityBiagio

© 2022 - 2024 — McMap. All rights reserved.