【问题标题】:Cannot detect when outgoing call is answered in Android无法检测到在 Android 中何时接听了拨出电话
【发布时间】:2012-10-30 07:17:25
【问题描述】:

为了检测拨出电话何时被接听,我尝试创建一个PhoneStateListener 并监听来自this questionTelephonyManagerCALL_STATE_RINGINGCALL_STATE_OFFHOOKCALL_STATE_IDLE,但似乎没有工作,如下所述。

首先,我在清单中注册了以下权限:

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

然后,一个名为OutCallLoggerBroadcastReceiver 在拨出电话时捕获NEW_OUTGOING_CALL 事件:

<receiver android:name=".listener.OutCallLogger">
    <intent-filter>
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
    </intent-filter>
</receiver>

接下来,我对OutCallLogger 的实现。我设置了一个名为noCallListenerYet 的布尔值,以避免在调用onReceive() 时将新的PhoneStateListener 附加到TelephonyManager

public class OutCallLogger extends BroadcastReceiver {

    private static boolean noCallListenerYet = true;

    @Override
    public void onReceive(final Context context, Intent intent) {
        number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
        if (noCallListenerYet) {
            final TelephonyManager tm = (TelephonyManager) context.getSystemService(
                    Context.TELEPHONY_SERVICE);
            tm.listen(new PhoneStateListener() {
                @Override
                public void onCallStateChanged(int state, String incomingNumber) {
                    switch (state) {
                        case TelephonyManager.CALL_STATE_RINGING:
                            Log.d(This.LOG_TAG, "RINGING");
                            break;
                        case TelephonyManager.CALL_STATE_OFFHOOK:
                            Log.d(This.LOG_TAG, "OFFHOOK");
                            break;
                        case TelephonyManager.CALL_STATE_IDLE:
                            Log.d(This.LOG_TAG, "IDLE");
                            break;
                        default:
                            Log.d(This.LOG_TAG, "Default: " + state);
                            break;
                    }
                }
            }, PhoneStateListener.LISTEN_CALL_STATE);
            noCallListenerYet = false;
        }
    }

}

现在,当我在我的设备中拨出电话时,CALL_STATE_RINGING 永远不会被调用。我总是只会在另一条线路开始响铃时打印出“IDLE”到“OFFHOOK”,什么也没有接听电话时,通话结束时再次打印“IDLE”。

我如何才能可靠地检测到在 Android 中何时接听了拨出电话,或者这是否可能?

【问题讨论】:

  • 你找到答案了吗!!!即使我有同样的问题!!!!帮帮我!!!
  • 大家看看这个链接codingaffairs.blogspot.com/2016/02/…
  • 我也有同样的问题你有解决办法吗?
  • 伙计们,任何解决方案... 到目前为止,没有任何事件可以在接听电话时触发。

标签: android broadcastreceiver


【解决方案1】:

从 Android 5.0 开始,这对于系统应用程序是可能的。但是您需要使用隐藏的 Android API。

我让它像这样工作:

<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<receiver android:name=".listener.OutCallLogger">
    <intent-filter>
        <action android:name="android.intent.action.PRECISE_CALL_STATE" />
    </intent-filter>
</receiver>
public class OutCallLogger extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        switch (intent.getIntExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, -2) {
            case PreciseCallState.PRECISE_CALL_STATE_IDLE:
                Log.d(This.LOG_TAG, "IDLE");
                break;
            case PreciseCallState.PRECISE_CALL_STATE_DIALING:
                Log.d(This.LOG_TAG, "DIALING");
                break;
            case PreciseCallState.PRECISE_CALL_STATE_ALERTING:
                Log.d(This.LOG_TAG, "ALERTING");
                break;
            case PreciseCallState.PRECISE_CALL_STATE_ACTIVE:
                Log.d(This.LOG_TAG, "ACTIVE");
                break;
        }
    }
}

您可以在PreciseCallState.java 中找到所有可能的调用状态,并在TelephonyRegistry.java 中找到意图包含的所有附加信息。

【讨论】:

  • 这仅适用于 Android 5.0 吗?还是也支持4.0-4.4?
  • @stack 此解决方案仅适用于 Android >= 5.0。电话 API 在 Android 5.0 中为 changed 以实现这一点。 (所需权限的保护级别后来是changed from dangerous to signature|system
  • 这对来电也有效吗??
  • 无法解析符号TelephonyManager.EXTRA_FOREGROUND_CALL_STATE!
  • @MuhammadBabar 我使用它的方式是使用反射在运行时加载符号,并且我仅将我的应用程序作为系统应用程序安装在有根手机上。我没有尝试自己替换任何 .jar,但我认为您需要在编译时链接到 android-with-hidden-api.jar
【解决方案2】:

看起来只有来电才能达到 RINGING 状态。拨出电话从 IDLE 变为 OFFHOOK,因此查看电话状态可能无法实现。

我认为可以使用内部函数,看看这个:What does the different Call states in the Android telephony stack represent?

【讨论】:

    【解决方案3】:

    也许尝试使用 CallManager?查看http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/com/android/internal/telephony/CallManager.java。我还在我计算机上的 SDK 文件中找到了 CallManager.java。链接页面中的以下文本似乎很有希望:

    Register for getting notifications for change in the Call State Call.State This is 
    called PreciseCallState because the call state is more precise than the Phone.State 
    which can be obtained using the android.telephony.PhoneStateListener Resulting events 
    will have an AsyncResult in Message.obj. AsyncResult.userData will be set to the obj 
    argument here. The h parameter is held only by a weak reference.
    
    1051
    1052    public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
    1053        mPreciseCallStateRegistrants.addUnique(h, what, obj);
    1054    }
    

    我没有尝试编写任何代码,所以真的不知道它是否可以做你想要的,但我很想知道。

    【讨论】:

    • 知道如何访问这个 CallManager 类或相关函数吗?我试过phonestatelisteners,也试过动态读取通话记录,但不起作用。想要识别拨出电话何时被接听,并在接听时播放音频文件。显然没有办法做同样的事情(?)
    • 根据这个答案,这种方法不起作用stackoverflow.com/a/5495850/3441905
    • 不过这个问题也很有用stackoverflow.com/questions/4745899/…
    【解决方案4】:

    请关注:

    tm.listen(new PhoneStateListener() {
                @Override
                public void onCallStateChanged(int state, String incomingNumber) {
                    switch (state) {
                        case TelephonyManager.CALL_STATE_RINGING:
                            Log.d(This.LOG_TAG, "RINGING");
                            break;
                        case TelephonyManager.CALL_STATE_OFFHOOK:
                            Log.d(This.LOG_TAG, "OFFHOOK");
                            break;
                        case TelephonyManager.CALL_STATE_IDLE:
                            Log.d(This.LOG_TAG, "IDLE");
                            break;
                        default:
                            Log.d(This.LOG_TAG, "Default: " + state);
                            break;
                    }
                }
            }, PhoneStateListener.LISTEN_CALL_STATE);
    

    你看到“incomingNumber”参数了吗?是的,只有当您的设备有来电时,该代码才能检测到您的电话状态。

    【讨论】:

    • 问题是拨出电话,而不是拨入电话。
    • 我知道,我只是指出为什么他的代码不能按照他的预期工作。
    【解决方案5】:

    您可以执行以下操作...不是很精确,但可以解决问题:

    1. 您将接收器用于 android.intent.action.NEW_OUTGOING_CALL 操作
    2. 当接收器被调用时,您将 NEW_OUTGOIN_CALL 状态和发生这种情况的时间(即 new Date().getTime())存储在某处(例如静态变量)
    3. 您为 android.intent.action.PHONE_STATE 使用另一个接收器,并在 onReceive 中执行以下操作:

      if (intent.getAction().equals("android.intent.action.PHONE_STATE")) {
          TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
          telephony.listen(new PhoneStateListener() {
              public void onCallStateChanged(int state, String incomingNumber) {
                    switch(state) {
                      case TelephonyManager.CALL_STATE_IDLE:
                      break;
                      case TelephonyManager.CALL_STATE_OFFHOOK:
                      break;
                      case TelephonyManager.CALL_STATE_RINGING:
                      break;
                     }
              } 
          }, PhoneStateListener.LISTEN_CALL_STATE);
      
      }
      

    在 CALL_STATE_OFFHOOK 情况下,您检查最后存储的状态是 NEW_OUTGOING_CALL 并且不超过 aprox。自上次状态更改以来已过去 10 秒。这意味着电话最多在 10 秒前发起呼叫,现在他处于摘机状态(即通话中),没有经过空闲或振铃。这可能意味着呼叫已被接听。

    【讨论】:

    • 我刚试过这种方法,恐怕行不通。这是我的代码:pastie.org/5846783。我创建了一个广播接收器,它接受你提到的两个意图。我什至包括了以EXTRA_STATE 开头的呼叫状态,以确保。但是,拨出电话永远不会通过EXTRA_STATE_RINGINGCALL_STATE_RINGING--它会立即转到EXTRA- 和CALL_STATE_OFFHOOK,当拨出电话被应答时不会调用任何内容,并且EXTRA- 和@结束时调用 987654329@。在 Android 4.2.1 上使用 Nexus 4 进行测试。
    • 这不起作用。当你点击“发送”按钮时你会得到 CALL_STATE_OFFHOOK,但当对方接听电话时你什么也得不到。
    【解决方案6】:

    您的答案是您在 OutGoingCallReceiver 中实现了 CallStateListener ,这是错误的。你必须在 PhoneStateListener 中实现 CallStateListener

    我在之前的项目中也尝试过这个东西,我遇到了同样的问题,然后我解决了如下。我参加了以下 3 节课。

    1. AutoCallReceiver:向PhoneStateListener.LISTEN_CALL_STATE注册 TelephonyManager

    2. CallStateListener 监听三个状态为TelephonyManager.CALL_STATE_IDLE,TelephonyManager.CALL_STATE_OFFHOOK,TelephonyManager.CALL_STATE_RINGING

    3.OutGoingCallReceiver 处理去电

    public class OutGoingCallReceiver extends BroadcastReceiver {  
        /* onReceive will execute on out going call */
        @Override  
        public void onReceive(Context context, Intent intent) {  
            Toast.makeText(context, "OutGoingCallReceiver", Toast.LENGTH_SHORT).show();
        }
    }
    

    public class CallStateListener extends PhoneStateListener {  
        String number=""; // variable for storing incoming/outgoing number
        Context mContext; // Application Context
    
        //Constructor that will accept Application context as argument
        public CallStateListener(Context context) {
            mContext=context;
        }
    
        // This function will automatically invoke when call state changed
        public void onCallStateChanged(int state,String incomingNumber)
        {
            boolean end_call_state=false; // this variable when true indicate that call is disconnected
            switch(state) {
                case TelephonyManager.CALL_STATE_IDLE:  
                    // Handling Call disconnect state after incoming/outgoing call 
                    Toast.makeText(mContext, "idle", Toast.LENGTH_SHORT).show();
                    break;  
    
                case TelephonyManager.CALL_STATE_OFFHOOK:     
                    // Handling outgoing call  
                    Toast.makeText(mContext, "OFFHOOK", Toast.LENGTH_SHORT).show();
                    // saving outgoing call state so that after disconnect idle state can act accordingly
                    break;  
    
                case TelephonyManager.CALL_STATE_RINGING: 
                    Toast.makeText(mContext, "RINGING", Toast.LENGTH_SHORT).show();     
                    break;
            }
        }
    }
    

    public class AutoCallReceiver extends BroadcastReceiver {       
        /* onReceive will execute on call state change */
        @Override  
        public void onReceive(Context context, Intent intent) { 
    
        // Instantiating PhoneStateListener
        CallStateListener phoneListener=new CallStateListener(context);  
    
        // Instantiating TelephonyManager
            TelephonyManager telephony = (TelephonyManager)   
                             context.getSystemService(Context.TELEPHONY_SERVICE);  
    
                // Registering the telephony to listen CALL STATE change
            telephony.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);  
        }
    }
    

    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <application ...>
        <receiver android:name=".OutGoingCallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>
        <receiver android:name=".AutoCallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
    </application>
    

    【讨论】:

    • 这不正是我所做的,只是不太具体吗?
    • 对不起,我已经更新了我的答案,你必须在PhoneStateListener中实现CallStateListener
    • 首先,AutoCallReceiver 不接电话。其次,AutoCallReceiver 的内部仍然完全符合我在问题中所写的内容。
    • AutoCallReceiver 仅用于调用 phonestateListener 捕获空闲、offshook 和振铃状态,并且您在 OutGoingCallReceiver 中实现了这一点,这是错误的
    • 我从您的代码中了解到,当拨出电话时,它会触发NEW_OUTGOING_CALL,因此它会转到OutGoingCallReceiver,它只会显示Toast。然后,当来电到达时(这不是所述的问题),PHONE_STATE 意图被触发并且应用程序在AutoCallReceiver 甚至执行之前崩溃,因为您没有包含READ_PHONE_STATE 权限。但无论如何,您只需将PhoneStateListener 放在外部类中,而我将其编写为匿名类。因此,预期的效果是相同的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多