实测可用。
测试手机:
MeiZu MX4 Android 5.1
HuaWei Mate 8 Android 7.0
-----------------------------------------------------------
Here's what I use to do this:
AndroidManifest:
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
EDIT: Updated to simpler code, as I've reworked the class for my own use