Jak wykryć połączenia przychodzące na urządzeniu z Androidem?

136

Próbuję stworzyć aplikację, która w przypadku połączenia przychodzącego na telefon chcę wykryć numer. Poniżej znajduje się to, co próbowałem, ale nie wykrywa połączeń przychodzących.

Chcę działać MainActivityw tle, jak mogę to zrobić?

Udzieliłem pozwolenia w manifestpliku.

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

Czy jest jeszcze coś, co powinienem podać w manifeście?

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_layout);
   }

   public class myPhoneStateChangeListener extends PhoneStateListener {
       @Override
       public void onCallStateChanged(int state, String incomingNumber) {
           super.onCallStateChanged(state, incomingNumber);
           if (state == TelephonyManager.CALL_STATE_RINGING) {
               String phoneNumber =   incomingNumber;
           }
       }
   }
}
Jesbin MJ
źródło
co powinniśmy zrobić dla Androida P
Ahmad Arslan

Odpowiedzi:

339

Oto, czego używam, aby to zrobić:

Oczywisty:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

<!--This part is inside the application-->
    <receiver android:name=".CallReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
    </receiver>

Mój podstawowy detektor połączeń wielokrotnego użytku

package com.gabesechan.android.reusable.receivers;

import java.util.Date;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;

public abstract class PhonecallReceiver extends BroadcastReceiver {

    //The receiver will be recreated whenever android feels like it.  We need a static variable to remember data between instantiations

    private static int lastState = TelephonyManager.CALL_STATE_IDLE;
    private static Date callStartTime;
    private static boolean isIncoming;
    private static String savedNumber;  //because the passed incoming is only valid in ringing


    @Override
    public void onReceive(Context context, Intent intent) {

        //We listen to two intents.  The new outgoing call only tells us of an outgoing call.  We use it to get the number.
        if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
            savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
        }
        else{
            String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
            String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
            int state = 0;
            if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
                state = TelephonyManager.CALL_STATE_IDLE;
            }
            else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
                state = TelephonyManager.CALL_STATE_OFFHOOK;
            }
            else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
                state = TelephonyManager.CALL_STATE_RINGING;
            }


            onCallStateChanged(context, state, number);
        }
    }

    //Derived classes should override these to respond to specific events of interest
    protected abstract void onIncomingCallReceived(Context ctx, String number, Date start);
    protected abstract void onIncomingCallAnswered(Context ctx, String number, Date start);
    protected abstract void onIncomingCallEnded(Context ctx, String number, Date start, Date end);

    protected abstract void onOutgoingCallStarted(Context ctx, String number, Date start);      
    protected abstract void onOutgoingCallEnded(Context ctx, String number, Date start, Date end);

    protected abstract void onMissedCall(Context ctx, String number, Date start);

    //Deals with actual events

    //Incoming call-  goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
    //Outgoing call-  goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
    public void onCallStateChanged(Context context, int state, String number) {
        if(lastState == state){
            //No change, debounce extras
            return;
        }
        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                isIncoming = true;
                callStartTime = new Date();
                savedNumber = number;
                onIncomingCallReceived(context, number, callStartTime);
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                //Transition of ringing->offhook are pickups of incoming calls.  Nothing done on them
                if(lastState != TelephonyManager.CALL_STATE_RINGING){
                    isIncoming = false;
                    callStartTime = new Date();
                    onOutgoingCallStarted(context, savedNumber, callStartTime);                     
                }
                else
                {
                    isIncoming = true;
                    callStartTime = new Date();
                    onIncomingCallAnswered(context, savedNumber, callStartTime); 
                }

                break;
            case TelephonyManager.CALL_STATE_IDLE:
                //Went to idle-  this is the end of a call.  What type depends on previous state(s)
                if(lastState == TelephonyManager.CALL_STATE_RINGING){
                    //Ring but no pickup-  a miss
                    onMissedCall(context, savedNumber, callStartTime);
                }
                else if(isIncoming){
                    onIncomingCallEnded(context, savedNumber, callStartTime, new Date());                       
                }
                else{
                    onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());                                               
                }
                break;
        }
        lastState = state;
    }
}

Następnie, aby go użyć, po prostu wyprowadź z niego klasę i zaimplementuj kilka prostych funkcji, niezależnie od typów wywołań, na których Ci zależy:

public class CallReceiver extends PhonecallReceiver {

    @Override
    protected void onIncomingCallReceived(Context ctx, String number, Date start)
    {
        //
    }

    @Override
    protected void onIncomingCallAnswered(Context ctx, String number, Date start)
    {
        //
    }

    @Override
    protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end)
    {
        //
    }

    @Override
    protected void onOutgoingCallStarted(Context ctx, String number, Date start)
    {
        //
    } 

    @Override 
    protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end)
    {
        //
    }

    @Override
    protected void onMissedCall(Context ctx, String number, Date start)
    {
        //
    }

}

Ponadto możesz zobaczyć napisane przeze mnie, dlaczego kod wygląda tak, jak jest na moim blogu . Link do streszczenia: https://gist.github.com/ftvs/e61ccb039f511eb288ee

EDYCJA: Zaktualizowano do prostszego kodu, ponieważ przerobiłem klasę na własny użytek

Gabe Sechan
źródło
2
Ten kod nic nie wyświetla. To, co robi, to dzwoni do ciebie, gdy połączenie wychodzące zaczyna się / kończy i przekazuje ci numer, godzinę rozpoczęcia i czas zakończenia. Właściwie pokazanie tego jest twoją pracą, ponieważ nie mam możliwości dowiedzenia się, jak chcesz to zrobić.
Gabe Sechan
3
@GabeSechan: Niesamowite! czy możesz mi pomóc w radzeniu sobie z sytuacją oczekiwania na połączenie?
Mehul Joisar
6
Aby dodać do tego, nie działało, gdy aplikacja nie była na pierwszym planie ani w tle, dopóki nie dodałem tego w odbiorniku: "android: enabled =" true "
Rajat Sharma
1
Zmienne statyczne pozostaną dostępne, dopóki aplikacja nie zostanie wyrzucona z pamięci (co może trwać dość długo, w zależności od tego, czy usługi są uruchomione i ogólne warunki pamięci telefonu). Ale tak, można je zgubić. Możesz zapisać go na dysku, powiedzmy przez wspólne preferencje, ale może to również spowodować fałszywe wyniki - uniemożliwiłoby to prawidłowe wyczyszczenie danych w kilku przypadkach, na przykład przy ponownym uruchomieniu telefonu. W moim przypadku rzadkie dane zerowe i utracone były lepsze niż nieprawidłowe dane. Nie krępuj się dostosowywać do swoich potrzeb.
Gabe Sechan
1
@GabeSechan: Wygląda na to, że jest w nim błąd. lastState nie powinien być inicjalizowany na CALL_STATE_IDLE. Brakuje mi kilku wywołań, gdy moja aplikacja zostaje zabita w obecnym stanie RINGING. Ponieważ gdy IDLEponownie staje się po zakończeniu wywołania, zmienna statyczna jest ponownie inicjowana CALL_STATE_IDLEi nie robi nic. Więc tracimy odniesienie do lastState.
Heisenberg
23
private MyPhoneStateListener phoneStateListener = new MyPhoneStateListener();

rejestrować

TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);

i wyrejestrować się

TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
stinepike
źródło
gdzie powinienem tego używać w działalności głównej?
Jesbin MJ
umieść to w klasie, w której słuchasz zmiany .. normalnie rejestry są wykonywane w oncreate i wyrejestruj w ondestroy .. deklaruj obiekt globalnie w klasie
stinepike
Ta opcja nie wymaga uprawnień.
Mike
Chociaż rozwiązanie Gabe'a lepiej nadaje się do bardziej uciążliwych funkcji, np. aplikacji typu Viber, jest to najlepsze rozwiązanie dla tych, którzy muszą po prostu reagować na działania użytkownika związane z rozmową telefoniczną. Żądanie pozwolenia na wykonanie w takich przypadkach jest najprawdopodobniej przesadą i może nie zostać dobrze odebrane przez użytkownika.
Bosfera
1
Chcę coś zrobić, kiedy nadchodzi połączenie, jak zrobić z tym kodem?
Noor Hossain
14

Z Androidem P - Api Level 28: Musisz uzyskać uprawnienie READ_CALL_LOG

Ograniczony dostęp do dzienników połączeń

Android P porusza CALL_LOG, READ_CALL_LOG, WRITE_CALL_LOGoraz PROCESS_OUTGOING_CALLSuprawnienia z PHONEgrupy uprawnień do nowej CALL_LOGgrupy uprawnień. Ta grupa zapewnia użytkownikom lepszą kontrolę i widoczność aplikacji, które potrzebują dostępu do poufnych informacji o połączeniach telefonicznych, takich jak odczytywanie zapisów rozmów telefonicznych i identyfikowanie numerów telefonów.

Aby odczytać liczby z akcji intencji PHONE_STATE, potrzebujesz zarówno READ_CALL_LOGpozwolenia, jak i READ_PHONE_STATEpozwolenia . Aby czytać numery z onCallStateChanged(), potrzebujesz teraz READ_CALL_LOGtylko pozwolenia. Nie potrzebujesz już READ_PHONE_STATEpozwolenia.

atasoyh
źródło
dla tych, którzy dodają tylko READ_CALL_LOGw AndroidManifest.xmlcentrum uwagi na dodanie wniosek o pozwolenie MainActivity.
Piyush
13

AKTUALIZACJA: Naprawdę niesamowity kod opublikowany przez Gabe Sechana nie działa, chyba że wyraźnie poprosisz użytkownika o przyznanie niezbędnych uprawnień. Oto kod, który możesz umieścić w swojej głównej aktywności, aby zażądać tych uprawnień:

    if (getApplicationContext().checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission has not been granted, therefore prompt the user to grant permission
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.READ_PHONE_STATE},
                MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
    }

    if (getApplicationContext().checkSelfPermission(Manifest.permission.PROCESS_OUTGOING_CALLS)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission has not been granted, therefore prompt the user to grant permission
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS},
                MY_PERMISSIONS_REQUEST_PROCESS_OUTGOING_CALLS);
    }

RÓWNIEŻ: Jak ktoś wspomniał w komentarzu pod postem Gabe'a , musisz dodać mały fragment kodu android:enabled="truedo odbiornika, aby wykryć połączenia przychodzące, gdy aplikacja nie działa obecnie na pierwszym planie:

    <!--This part is inside the application-->
    <receiver android:name=".CallReceiver" android:enabled="true">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
    </receiver>
topherPedersen
źródło
A co, jeśli aplikacja nie ma żadnej Aktywności, a jedynie odbiornik i usługę. Następnie gdzie piszemy ten kod, aby uzyskać pozwolenie od użytkownika, ponieważ odbiorca transmisji nie zostanie wywołany, dopóki nie zostanie udzielone takie pozwolenie.
user2779311
2
Przynajmniej potrzebujesz MainActivity, nawet jeśli jest otwarty tylko raz. Weźmy na przykład moją aplikację blokującą połączenia RoboStop: Kiedy użytkownik pobierze aplikację po raz pierwszy, a następnie kliknie ikonę aplikacji, aby uruchomić aplikację, zostanie wyświetlony monit o przyznanie mojej aplikacji niezbędnych uprawnień. Aplikacja posiada również przycisk do włączania / wyłączania blokowania połączeń, ale użytkownik nie musi ponownie uruchamiać aplikacji / działania, blokowanie połączeń będzie odbywać się w tle bez konieczności ponownego uruchamiania aplikacji / działania przez użytkownika.
topherPedersen
Ci, którzy mają problem z zaimplementowaniem tego, skorzystaj z tego samouczka studytutorial.pl/…
Prasath
5

może to pomóc, a także dodać wymagane uprawnienia

public class PhoneListener extends PhoneStateListener
{
    private Context context;
    public static String getincomno;

    public PhoneListener(Context c) {
        Log.i("CallRecorder", "PhoneListener constructor");
        context = c;
    }

    public void onCallStateChanged (int state, String incomingNumber)
    {

        if(!TextUtils.isEmpty(incomingNumber)){
        // here for Outgoing number make null to get incoming number
        CallBroadcastReceiver.numberToCall = null;
        getincomno = incomingNumber;
        }

        switch (state) {
        case TelephonyManager.CALL_STATE_IDLE:

            break;
        case TelephonyManager.CALL_STATE_RINGING:
            Log.d("CallRecorder", "CALL_STATE_RINGING");
            break;
        case TelephonyManager.CALL_STATE_OFFHOOK:

            break;
        }
    }
}
Ankitkumar Makwana
źródło
2
to wydaje się w porządku. Ale jak mogę to wykorzystać z aktywności? proszę powiedz mi szczegóły
Amir
2

Oto prosta metoda, dzięki której można uniknąć stosowania PhonestateListeneri innych komplikacji.
Więc jesteśmy otrzymaniu 3 wydarzenia z android, takich jak RINGING, OFFHOOKi IDLE. A także w celu uzyskania wszelkich możliwych stan połączenia, musimy określić nasze własne stany podoba RINGING, OFFHOOK, IDLE, FIRST_CALL_RINGING, SECOND_CALL_RINGING. Potrafi obsłużyć wszystkie stany w rozmowie telefonicznej.
Pomyśl w taki sposób, że otrzymujemy zdarzenia z Androida i zdefiniujemy nasze stany na wezwanie. Zobacz kod.

public class CallListening  extends BroadcastReceiver {
    private static final String TAG ="broadcast_intent";
    public static String incoming_number;
    private String current_state,previus_state,event;
    public static Boolean dialog= false;
    private Context context;
    private SharedPreferences sp,sp1;
    private SharedPreferences.Editor spEditor,spEditor1;
    public void onReceive(Context context, Intent intent) {
        //Log.d("intent_log", "Intent" + intent);
        dialog=true;
        this.context = context;
        event = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        incoming_number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
        Log.d(TAG, "The received event : "+event+", incoming_number : " + incoming_number);
        previus_state = getCallState(context);
        current_state = "IDLE";
        if(incoming_number!=null){
            updateIncomingNumber(incoming_number,context);
        }else {
            incoming_number=getIncomingNumber(context);
        }
        switch (event) {
            case "RINGING":
                Log.d(TAG, "State : Ringing, incoming_number : " + incoming_number);
            if((previus_state.equals("IDLE")) || (previus_state.equals("FIRST_CALL_RINGING"))){
                    current_state ="FIRST_CALL_RINGING";
                }
                if((previus_state.equals("OFFHOOK"))||(previus_state.equals("SECOND_CALL_RINGING"))){
                    current_state = "SECOND_CALL_RINGING";
                }

                break;
            case "OFFHOOK":
                Log.d(TAG, "State : offhook, incoming_number : " + incoming_number);
                if((previus_state.equals("IDLE")) ||(previus_state.equals("FIRST_CALL_RINGING")) || previus_state.equals("OFFHOOK")){
                    current_state = "OFFHOOK";
                }
                if(previus_state.equals("SECOND_CALL_RINGING")){
                    current_state ="OFFHOOK";
                    startDialog(context);
                }
                break;
            case "IDLE":
                Log.d(TAG, "State : idle and  incoming_number : " + incoming_number);
                if((previus_state.equals("OFFHOOK")) || (previus_state.equals("SECOND_CALL_RINGING")) || (previus_state.equals("IDLE"))){
                    current_state="IDLE";
                }
                if(previus_state.equals("FIRST_CALL_RINGING")){
                    current_state = "IDLE";
                    startDialog(context);
                }
                updateIncomingNumber("no_number",context);
                Log.d(TAG,"stored incoming number flushed");
                break;
        }
        if(!current_state.equals(previus_state)){
            Log.d(TAG, "Updating  state from "+previus_state +" to "+current_state);
            updateCallState(current_state,context);

        }
    }
    public void startDialog(Context context) {
        Log.d(TAG,"Starting Dialog box");
        Intent intent1 = new Intent(context, NotifyHangup.class);
        intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent1);

    }
    public void updateCallState(String state,Context context){
        sp = PreferenceManager.getDefaultSharedPreferences(context);
        spEditor = sp.edit();
        spEditor.putString("call_state", state);
        spEditor.commit();
        Log.d(TAG, "state updated");

    }
    public void updateIncomingNumber(String inc_num,Context context){
        sp = PreferenceManager.getDefaultSharedPreferences(context);
        spEditor = sp.edit();
        spEditor.putString("inc_num", inc_num);
        spEditor.commit();
        Log.d(TAG, "incoming number updated");
    }
    public String getCallState(Context context){
        sp1 = PreferenceManager.getDefaultSharedPreferences(context);
        String st =sp1.getString("call_state", "IDLE");
        Log.d(TAG,"get previous state as :"+st);
        return st;
    }
    public String getIncomingNumber(Context context){
        sp1 = PreferenceManager.getDefaultSharedPreferences(context);
        String st =sp1.getString("inc_num", "no_num");
        Log.d(TAG,"get incoming number as :"+st);
        return st;
    }
}
ARUNBALAN NV
źródło
0

@ Gabe Sechan, dziękujemy za kod. Działa dobrze, z wyjątkiem onOutgoingCallEnded(). Nigdy nie jest wykonywany. Testowane telefony to Samsung S5 i Trendy. Myślę, że są 2 błędy.

1: brakuje pary nawiasów.

case TelephonyManager.CALL_STATE_IDLE: 
    // Went to idle-  this is the end of a call.  What type depends on previous state(s)
    if (lastState == TelephonyManager.CALL_STATE_RINGING) {
        // Ring but no pickup-  a miss
        onMissedCall(context, savedNumber, callStartTime);
    } else {
        // this one is missing
        if(isIncoming){
            onIncomingCallEnded(context, savedNumber, callStartTime, new Date());                       
        } else {
            onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());                                               
        }
    }
    // this one is missing
    break;

2: lastStatenie jest aktualizowany przez, statejeśli znajduje się na końcu funkcji. W pierwszym wierszu tej funkcji należy go zastąpić znakiem

public void onCallStateChanged(Context context, int state, String number) {
    int lastStateTemp = lastState;
    lastState = state;
    // todo replace all the "lastState" by lastStateTemp from here.
    if (lastStateTemp  == state) {
        //No change, debounce extras
        return;
    }
    //....
}

Dodatkowo umieściłem lastStatei savedNumberwe wspólnych preferencjach, jak sugerowałeś.

Właśnie przetestowałem to z powyższymi zmianami. Naprawiono błąd przynajmniej w moich telefonach.

Diiiiii
źródło
0

Proszę użyć poniższego kodu. Pomoże Ci to uzyskać numer przychodzący z innymi szczegółami połączenia.

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >

<TextView
    android:id="@+id/call"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:text="@string/hello_world" />

</RelativeLayout>

MainActivity.java

public class MainActivity extends Activity {

private static final int MISSED_CALL_TYPE = 0;
private TextView txtcall;

@Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    txtcall = (TextView) findViewById(R.id.call);

    StringBuffer sb = new StringBuffer();
    Cursor managedCursor = managedQuery(CallLog.Calls.CONTENT_URI, null,
            null, null, null);
    int number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER);
    int type = managedCursor.getColumnIndex(CallLog.Calls.TYPE);
    int date = managedCursor.getColumnIndex(CallLog.Calls.DATE);
    int duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION);
    sb.append("Call Details :");
    while (managedCursor.moveToNext()) {
        String phNumber = managedCursor.getString(number);
        String callType = managedCursor.getString(type);
        String callDate = managedCursor.getString(date);
        Date callDayTime = new Date(Long.valueOf(callDate));
        String callDuration = managedCursor.getString(duration);
        String dir = null;
        int dircode = Integer.parseInt(callType);
        switch (dircode) {

        case CallLog.Calls.OUTGOING_TYPE:
            dir = "OUTGOING";
            break;

        case CallLog.Calls.INCOMING_TYPE:
            dir = "INCOMING";
            break;

        case CallLog.Calls.MISSED_TYPE:
            dir = "MISSED";
            break;
        }
        sb.append("\nPhone Number:--- " + phNumber + " \nCall Type:--- "
                + dir + " \nCall Date:--- " + callDayTime
                + " \nCall duration in sec :--- " + callDuration);
        sb.append("\n----------------------------------");
    }
    managedCursor.close();
    txtcall.setText(sb);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}

} 

oraz w swoim manifeście prośby o następujące uprawnienia:

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
user2173696
źródło
READ_LOGS dzięki temu Twoja aplikacja zostanie zablokowana w Sklepie Play
Duna
0

Potrzebujesz ACTION_PHONE_STATE_CHANGEDodbiornika BroadcastReceiver dla To zadzwoni do twojego odebranego połączenia, gdy stan telefonu zmieni się z bezczynności, dzwonienia, podniesienia słuchawki, więc z poprzedniej wartości i nowej wartości można wykryć, czy jest to połączenie przychodzące / wychodzące.

Wymagane pozwolenie to:

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

Ale jeśli chcesz również otrzymać EXTRA_INCOMING_NUMBER w tej transmisji, potrzebujesz innego uprawnienia: „android.permission.READ_CALL_LOG”

A kod coś takiego:

val receiver: BroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "onReceive")
    }
}

override fun onResume() {
    val filter = IntentFilter()
    filter.addAction("android.intent.action.PHONE_STATE")
    registerReceiver(receiver, filter)
    super.onResume()
}

override fun onPause() {
    unregisterReceiver(receiver)
    super.onPause()
}

aw klasie odbiornika możemy uzyskać aktualny stan, czytając intencję w następujący sposób:

intent.extras["state"]

wynikiem dodatków może być:

DZWONEK -> Jeśli dzwoni Twój telefon

OFFHOOK -> Jeśli rozmawiasz z kimś (połączenie przychodzące lub wychodzące)

IDLE -> jeśli połączenie zakończone (połączenie przychodzące lub wychodzące)

W przypadku transmisji PHONE_STATE nie musimy używać uprawnienia PROCESS_OUTGOING_CALLS ani wycofanej akcji NEW_OUTGOING_CALL.

FarshidABZ
źródło