160 votes

Comment détecter les appels entrants, dans un appareil Android ?

J'essaie de faire une application comme, quand un appel arrive sur le téléphone, je veux détecter le numéro. Voici ce que j'ai essayé, mais ça ne détecte pas les appels entrants.

Je veux diriger mon MainActivity en arrière-plan, comment puis-je faire ?

J'avais donné la permission en manifest archivo.

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

Y a-t-il autre chose que je dois fournir dans le manifeste ?

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;
           }
       }
   }
}

368voto

Gabe Sechan Points 23732

Voici ce que j'utilise pour faire ça :

Manifeste :

<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>

Mon détecteur d'appels réutilisables de base

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;
    }
}

Ensuite, pour l'utiliser, il suffit de dériver une classe à partir de celle-ci et d'implémenter quelques fonctions simples, quels que soient les types d'appel qui vous intéressent :

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)
    {
        //
    }

}

En outre, vous pouvez voir un article que j'ai rédigé sur la raison pour laquelle le code est tel qu'il est sur mon site Web. blog . Lien Gist : https://gist.github.com/ftvs/e61ccb039f511eb288ee

EDIT : Mise à jour vers un code plus simple, car j'ai retravaillé la classe pour mon propre usage.

26voto

StinePike Points 20501
private MyPhoneStateListener phoneStateListener = new MyPhoneStateListener();

pour s'inscrire

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

et pour désenregistrer

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

19voto

atasoyh Points 2655

Avec Android P - Niveau Api 28 : Vous devez obtenir l'autorisation READ_CALL_LOG.

Accès restreint aux journaux d'appels

Android P déplace le CALL_LOG , READ_CALL_LOG , WRITE_CALL_LOG et PROCESS_OUTGOING_CALLS les autorisations de l PHONE au nouveau groupe de permission CALL_LOG groupe de permission. Ce groupe permet aux utilisateurs d'avoir un meilleur contrôle et une meilleure visibilité sur les applications qui ont besoin d'accéder à des informations sensibles sur les appels téléphoniques, comme la lecture des enregistrements d'appels téléphoniques et l'identification des numéros de téléphone.

Pour lire les numéros de l'action d'intention PHONE_STATE, vous avez besoin à la fois du READ_CALL_LOG l'autorisation et la READ_PHONE_STATE autorisation . Pour lire les chiffres de onCallStateChanged() vous avez maintenant besoin du READ_CALL_LOG uniquement sur autorisation. Vous n'avez plus besoin de la READ_PHONE_STATE permission.

18voto

topherPedersen Points 250

UPDATE : Le code vraiment génial posté par Gabe Sechan ne fonctionne plus, sauf si vous demandez explicitement à l'utilisateur d'accorder les autorisations nécessaires. Voici un code que vous pouvez placer dans votre activité principale pour demander ces permissions :

    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);
    }

AUSSI : Comme quelqu'un l'a mentionné dans un commentaire ci-dessous Le message de Gabe vous devez ajouter un petit bout de code, android:enabled="true Les appels entrants sont ainsi détectés lorsque l'application n'est pas en cours d'exécution au premier plan :

    <!--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>

8voto

Jaitnium Points 118

Juste pour mettre à jour la réponse de Gabe Sechan. Si votre manifeste demande des permissions pour READ_CALL_LOG et READ_PHONE_STATE, onReceive appellera TWICE . L'un d'eux contient EXTRA_INCOMING_NUMBER et l'autre pas. Vous devez tester lequel des deux contient ce numéro et il peut apparaître dans n'importe quel ordre.

https://developer.Android.com/reference/Android/telephony/TelephonyManager.html#ACTION_PHONE_STATE_CHANGED

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X