简体   繁体   English

如何在 Android 设备中检测来电?

[英]How to detect incoming calls, in an Android device?

I'm trying to make an app like, when a call comes to the phone I want to detect the number.我正在尝试制作一个应用程序,例如,当电话打到我想检测号码时。 Below is what I tried, but it's not detecting incoming calls.以下是我尝试过的,但它没有检测到来电。

I want to run my MainActivity in background, how can I do that?我想在后台运行我的MainActivity ,我该怎么做?

I had given the permission in manifest file.我已经在manifest文件中给予了许可。

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

Is there anything else should I provide in the manifest?我还应该在清单中提供什么吗?

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

Here's what I use to do this:这是我用来执行此操作的方法:

Manifest:显现:

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

My base reusable call detector我的基本可重用呼叫检测器

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

Then to use it, simply derive a class from it and implement a few easy functions, whichever call types you care about:然后要使用它,只需从中派生一个类并实现一些简单的函数,无论您关心哪种调用类型:

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

}

In addition you can see a writeup I did on why the code is like it is on my blog .此外,您可以在我的博客上看到我写的一篇关于为什么代码像它一样的文章 Gist link: https://gist.github.com/ftvs/e61ccb039f511eb288ee要点链接: https : //gist.github.com/ftvs/e61ccb039f511eb288ee

EDIT: Updated to simpler code, as I've reworked the class for my own use编辑:更新为更简单的代码,因为我已经重新编写了该类供我自己使用

private MyPhoneStateListener phoneStateListener = new MyPhoneStateListener();

to register注册

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

and to unregister并取消注册

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

With Android P - Api Level 28: You need to get READ_CALL_LOG permission使用 Android P - Api Level 28: 您需要获得 READ_CALL_LOG 权限

Restricted access to call logs限制访问通话记录

Android P moves the CALL_LOG , READ_CALL_LOG , WRITE_CALL_LOG , and PROCESS_OUTGOING_CALLS permissions from the PHONE permission group to the new CALL_LOG permission group. Android P 将CALL_LOGREAD_CALL_LOGWRITE_CALL_LOGPROCESS_OUTGOING_CALLS权限从PHONE权限组移动到新的CALL_LOG权限组。 This group gives users better control and visibility to apps that need access to sensitive information about phone calls, such as reading phone call records and identifying phone numbers.该组使用户可以更好地控制和查看需要访问有关电话的敏感信息的应用程序,例如阅读电话记录和识别电话号码。

To read numbers from the PHONE_STATE intent action, you need both the READ_CALL_LOG permission and the READ_PHONE_STATE permission .要从 PHONE_STATE 意图操作读取数字,您需要READ_CALL_LOG权限和READ_PHONE_STATE权限 To read numbers from onCallStateChanged() , you now need the READ_CALL_LOG permission only.要从onCallStateChanged()读取数字,您现在只需要READ_CALL_LOG权限。 You no longer need the READ_PHONE_STATE permission.您不再需要READ_PHONE_STATE权限。

UPDATE: The really awesome code posted by Gabe Sechan no longer works unless you explicitly request the user to grant the necessary permissions.更新:除非您明确要求用户授予必要的权限,否则Gabe Sechan发布的非常棒的代码不再有效。 Here is some code that you can place in your main activity to request these 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);
    }

ALSO: As someone mentioned in a comment below Gabe's post , you have to add a little snippet of code, android:enabled="true , to the receiver in order to detect incoming calls when the app is not currently running in the foreground:另外:正如有人在Gabe 的帖子下方的评论中提到的那样,您必须向接收器添加一小段代码android:enabled="true ,以便在应用程序当前未在前台运行时检测来电:

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

Just to update Gabe Sechan's answer.只是为了更新 Gabe Sechan 的回答。 If your manifest asks for permissions to READ_CALL_LOG and READ_PHONE_STATE, onReceive will called TWICE .如果您的清单请求对 READ_CALL_LOG 和 READ_PHONE_STATE 的权限,则 onReceive 将调用TWICE One of which has EXTRA_INCOMING_NUMBER in it and the other doesn't.其中一个包含 EXTRA_INCOMING_NUMBER,另一个没有。 You have to test which has it and it can occur in any order.您必须测试哪个拥有它并且它可以以任何顺序发生。

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

this may helps you and also add require permision这可能会帮助您并添加需要权限

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

Here is a simple method which can avoid the use of PhonestateListener and other complications.这是一个简单的方法,可以避免使用PhonestateListener和其他并发症。
So here we are receiving the 3 events from android such as RINGING , OFFHOOK and IDLE .所以在这里我们从 android 接收 3 个事件,例如RINGINGOFFHOOKIDLE And in order to get the all possible state of call,we need to define our own states like RINGING , OFFHOOK , IDLE , FIRST_CALL_RINGING , SECOND_CALL_RINGING .为了获得所有可能的呼叫状态,我们需要定义我们自己的状态,如RINGINGOFFHOOKIDLEFIRST_CALL_RINGINGSECOND_CALL_RINGING It can handle every states in a phone call.它可以处理电话中的每个状态。
Please think in a way that we are receiving events from android and we will define our on call states.请以某种方式考虑我们正在从 android 接收事件,我们将定义我们的通话状态。 See the code.查看代码。

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

@Gabe Sechan, thanks for your code. @Gabe Sechan,感谢您的代码。 It works fine except the onOutgoingCallEnded() .除了onOutgoingCallEnded()之外,它工作正常。 It is never executed.它永远不会被执行。 Testing phones are Samsung S5 & Trendy.测试手机是三星 S5 和 Trendy。 There are 2 bugs I think.我认为有2个错误。

1: a pair of brackets is missing. 1:缺少一对括号。

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: lastState is not updated by the state if it is at the end of the function. 2: lastState如果在函数的末尾,则不会被state更新。 It should be replaced to the first line of this function by它应该被替换为这个函数的第一行

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

Additional I've put lastState and savedNumber into shared preference as you suggested.另外,我已按照您的建议将lastStatesavedNumber放入共享首选项中。

Just tested it with above changes.刚刚通过上述更改对其进行了测试。 Bug fixed at least on my phones.至少在我的手机上修复了错误。

Please use the below code.请使用以下代码。 It will help you to get the incoming number with other call details.它将帮助您获取具有其他呼叫详细信息的来电号码。

activity_main.xml活动_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主活动.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;
}

} 

and in your manifest request for following permissions:并在您的清单请求中获得以下权限:

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

You need a BroadcastReceiver for ACTION_PHONE_STATE_CHANGED This will call your received whenever the phone-state changes from idle, ringing, offhook so from the previous value and the new value you can detect if this is an incoming/outgoing call.您需要一个用于ACTION_PHONE_STATE_CHANGED的 BroadcastReceiver 这将在电话状态从空闲、振铃、摘机等状态从以前的值和新值变化时调用您的接收器,您可以检测到这是否是传入/传出呼叫。

Required permission would be:所需的权限是:

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

But if you also want to receive the EXTRA_INCOMING_NUMBER in that broadcast, you'll need another permission: "android.permission.READ_CALL_LOG"但是,如果您还想在该广播中接收 EXTRA_INCOMING_NUMBER,则需要另一个权限:“android.permission.READ_CALL_LOG”

And the code something like this:代码是这样的:

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

and in receiver class, we can get current state by reading intent like this:在接收器类中,我们可以通过读取意图来获取当前状态,如下所示:

intent.extras["state"]

the result of extras could be:额外的结果可能是:

RINGING -> If your phone is ringing RINGING -> 如果您的电话正在响铃

OFFHOOK -> If you are talking with someone (Incoming or Outcoming call)摘机 -> 如果您正在与某人通话(来电或来电)

IDLE -> if call ended (Incoming or Outcoming call)空闲 -> 如果通话结束(来电或来电)

With PHONE_STATE broadcast we don't need to use PROCESS_OUTGOING_CALLS permission or deprecated NEW_OUTGOING_CALL action.使用 PHONE_STATE 广播,我们不需要使用 PROCESS_OUTGOING_CALLS 权限或弃用的 NEW_OUTGOING_CALL 操作。

I fixed Gabe Sechan answer , I used the following code and it worked properly.我修复了Gabe Sechan 的答案,我使用了以下代码并且它工作正常。 I noticed when a receiver intent has the " incoming_number " key, I can get the phone number.我注意到当接收者意图具有“ incoming_number ”键时,我可以获得电话号码。 So I filtered incoming intent and used EXTRA_INCOMING_NUMBER and EXTRA_PHONE_NUMBER to get the phone number.所以我过滤了传入的意图并使用EXTRA_INCOMING_NUMBEREXTRA_PHONE_NUMBER来获取电话号码。

public class ChangeCallStateListener extends BroadcastReceiver {

    private static String lastState = TelephonyManager.EXTRA_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) {
        Log.d("CallObserver", "CallReceiver is starting ....");

        List<String> keyList = new ArrayList<>();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            keyList = new ArrayList<>(bundle.keySet());
            Log.e("CallObserver", "keys : " + keyList);
        }

        if (keyList.contains("incoming_number")) {
            String phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            String phoneIncomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
            String phoneOutgoingNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);

            String phoneNumber = phoneOutgoingNumber != null ? phoneOutgoingNumber : (phoneIncomingNumber != null ? phoneIncomingNumber : "");

            if (phoneState != null && phoneNumber != null) {
                if (lastState.equals(phoneState)) {
                    //No change, debounce extras
                    return;
                }
                Log.e("CallObserver", "phoneState = " + phoneState);
                if (TelephonyManager.EXTRA_STATE_RINGING.equals(phoneState)) {
                    isIncoming = true;
                    callStartTime = new Date();
                    //
                    lastState = TelephonyManager.EXTRA_STATE_RINGING;
                    if (phoneNumber != null) {
                        savedNumber = phoneNumber;
                    }


                    onIncomingCallStarted(context, savedNumber, callStartTime);
                } else if (TelephonyManager.EXTRA_STATE_IDLE.equals(phoneState)) {

                    if (lastState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                        //
                        lastState = TelephonyManager.EXTRA_STATE_IDLE;
                        onMissedCall(context, savedNumber, callStartTime);
                    } else {
                        if (isIncoming) {
                            //
                            lastState = TelephonyManager.EXTRA_STATE_IDLE;
                            onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
                        } else {
                            //
                            lastState = TelephonyManager.EXTRA_STATE_IDLE;
                            Log.d("CallObserver", "onOutgoingCallEnded called !! : ");
                            onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
                        }

                    }
                } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(phoneState)) {
                    if (lastState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                        isIncoming = true;
                    } else {
                        isIncoming = false;
                    }
                    callStartTime = new Date();
                    savedNumber = phoneNumber;
                    //
                    lastState = TelephonyManager.EXTRA_STATE_OFFHOOK;
                    onOutgoingCallStarted(context, savedNumber, callStartTime);
                }
            }
        }

    }


    protected void onIncomingCallStarted(Context ctx, String number, Date start) {
        Log.d("CallObserver", "onIncomingCallStarted  :  " + " number is  : " + number);
    }

    protected void onOutgoingCallStarted(Context ctx, String number, Date start) {
        Log.d("CallObserver", "onOutgoingCallStarted  :  " + " number is  : " + number);
    }

    protected void onIncomingCallEnded(Context context, String number, Date start, Date end) {
    }

    protected void onOutgoingCallEnded(Context context , String number, Date start, Date end) {
    }

    protected void onMissedCall(Context context, String number, Date start) {
    }

}
  • Don't forget to get run time permission.不要忘记获得运行时许可。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM