You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android拨号应用:通话中编程检测新来电并显示的方法

解决通话中检测新来电并展示的问题

核心问题分析

你当前的BroadcastReceiver方案失效,主要有两个关键原因:

  1. 实例状态无法持久:静态注册的BroadcastReceiver每次触发onReceive时都会创建新实例,成员变量lastState无法保存上一次的通话状态,导致无法判断「当前通话中+新来电」的场景。
  2. 系统版本限制:Android 10(API 29)及以上系统对ACTION_PHONE_STATE_CHANGED广播做了权限和行为限制,静态广播的可靠性大幅降低。

推荐解决方案:使用TelephonyCallback(API 29+)

从Android 10开始,官方推荐用TelephonyCallback替代广播监听通话状态,它能更可靠地获取实时通话状态,包括等待来电。

步骤1:申请必要权限

在AndroidManifest.xml中添加权限:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<!-- Android 12+ 需要通知权限才能在通话界面弹出提示 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 如果需要处理接听/挂断等待来电,需添加 -->
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />

注意:这些权限属于危险权限,需要在运行时动态申请。

步骤2:实现TelephonyCallback监听通话状态

在通话管理类或Activity中注册TelephonyCallback.CallStateListener:

class CallManager(private val context: Context) {
    private val telephonyManager = context.getSystemService(TelephonyManager::class.java)
    
    private val callStateListener = object : TelephonyCallback.CallStateListener() {
        override fun onCallStateChanged(state: Int) {
            when (state) {
                TelephonyManager.CALL_STATE_RINGING -> {
                    // 检查当前是否有活跃通话
                    val activeCalls = telephonyManager.activeCalls
                    if (activeCalls.isNotEmpty() && activeCalls.any { it.state == Call.STATE_ACTIVE }) {
                        // 存在活跃通话,触发新来电等待逻辑
                        notifyIncomingWaitingCall()
                    }
                }
            }
        }
    }

    // 注册监听
    fun registerCallStateListener() {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE)
            == PackageManager.PERMISSION_GRANTED
        ) {
            telephonyManager.registerTelephonyCallback(context.mainExecutor, callStateListener)
        }
    }

    // 注销监听
    fun unregisterCallStateListener() {
        telephonyManager.unregisterTelephonyCallback(callStateListener)
    }

    private fun notifyIncomingWaitingCall() {
        // 通过本地广播通知通话界面展示新来电
        val intent = Intent("ACTION_INCOMING_WAITING_CALL")
        intent.putExtra("PHONE_NUMBER", telephonyManager.line1Number)
        LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
    }
}

步骤3:在通话界面接收通知并展示

在通话Activity中注册本地广播接收器,处理等待来电事件:

class CallingActivity : AppCompatActivity() {
    private val waitingCallReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            intent?.getStringExtra("PHONE_NUMBER")?.let { phoneNumber ->
                showWaitingCallDialog(phoneNumber)
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_calling)
        
        // 注册本地广播
        LocalBroadcastManager.getInstance(this).registerReceiver(
            waitingCallReceiver,
            IntentFilter("ACTION_INCOMING_WAITING_CALL")
        )
        
        // 注册TelephonyCallback监听
        CallManager(this).registerCallStateListener()
    }

    override fun onDestroy() {
        super.onDestroy()
        LocalBroadcastManager.getInstance(this).unregisterReceiver(waitingCallReceiver)
        CallManager(this).unregisterCallStateListener()
    }

    private fun showWaitingCallDialog(phoneNumber: String) {
        AlertDialog.Builder(this)
            .setTitle("新来电等待")
            .setMessage("来电号码:$phoneNumber")
            .setPositiveButton("接听") { _, _ -> acceptWaitingCall() }
            .setNegativeButton("挂断") { _, _ -> rejectWaitingCall() }
            .show()
    }

    private fun acceptWaitingCall() {
        val telecomManager = getSystemService(TelecomManager::class.java)
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ANSWER_PHONE_CALLS)
            == PackageManager.PERMISSION_GRANTED
        ) {
            telecomManager.acceptRingingCall()
        }
    }

    private fun rejectWaitingCall() {
        val telecomManager = getSystemService(TelecomManager::class.java)
        telecomManager.endCall()
    }
}

兼容API 29以下的方案

如果需要支持Android 9及以下,需动态注册ACTION_PHONE_STATE_CHANGED广播,同时用Application全局变量保存通话状态:

class MyApp : Application() {
    var lastCallState = TelephonyManager.CALL_STATE_IDLE
}

修改BroadcastReceiver:

class CallActionReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val app = context.applicationContext as MyApp
        val stateStr = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
        val currentState = when (stateStr) {
            TelephonyManager.EXTRA_STATE_IDLE -> TelephonyManager.CALL_STATE_IDLE
            TelephonyManager.EXTRA_STATE_RINGING -> TelephonyManager.CALL_STATE_RINGING
            TelephonyManager.EXTRA_STATE_OFFHOOK -> TelephonyManager.CALL_STATE_OFFHOOK
            else -> TelephonyManager.CALL_STATE_IDLE
        }

        if (app.lastCallState != currentState) {
            when (currentState) {
                TelephonyManager.CALL_STATE_RINGING -> {
                    val telephonyManager = context.getSystemService(TelephonyManager::class.java)
                    if (app.lastCallState == TelephonyManager.CALL_STATE_OFFHOOK || 
                        telephonyManager.activeCalls.isNotEmpty()) {
                        LocalBroadcastManager.getInstance(context)
                            .sendBroadcast(Intent("ACTION_INCOMING_WAITING_CALL"))
                    }
                }
            }
        }
        app.lastCallState = currentState

        // 处理接听/挂断逻辑...
    }
}

在Activity中动态注册广播:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val filter = IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED)
    registerReceiver(CallActionReceiver(), filter)
}

内容的提问来源于stack exchange,提问作者Hamza Sheikh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:10:57