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

Android 10无需自定义拨号器获取呼出电话号码的技术问询

Android 10+ 非默认拨号应用获取呼出号码的替代方案

Android 10(API 29)开始,PROCESS_OUTGOING_CALLS权限被弃用,系统不再允许非默认拨号应用通过ACTION_NEW_OUTGOING_CALL广播或PhoneStateListener的onCallStateChanged直接获取呼出电话号码——这就是你当前代码中EXTRA_PHONE_NUMBER和回调参数为空的核心原因。以下是两种可行的替代方案:

方案一:读取通话记录(CallLog)

通过读取系统通话记录,在呼出电话结束后获取最近的呼出号码,无需用户设置默认拨号应用,但只能在通话完成后触发deeplink。

步骤:

  1. 添加权限:在Manifest中新增READ_CALL_LOG权限
  2. 监听通话状态:在通话从接通(OFFHOOK)变为空闲(IDLE)时,查询最近的呼出通话记录
  3. 匹配号码并跳转:获取号码后判断是否为目标号码,触发deeplink

代码修改:

Manifest权限补充:

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

修改OutgoingCallService中的PhoneStateListener:

@SuppressLint("MissingPermission")
private fun createPhoneStateListener(): PhoneStateListener {
    return object : PhoneStateListener() {
        private var lastCallState = TelephonyManager.CALL_STATE_IDLE

        override fun onCallStateChanged(state: Int, phoneNumber: String?) {
            super.onCallStateChanged(state, phoneNumber)
            when (state) {
                TelephonyManager.CALL_STATE_OFFHOOK -> {
                    lastCallState = state
                }
                TelephonyManager.CALL_STATE_IDLE -> {
                    // 仅在通话结束时触发查询
                    if (lastCallState == TelephonyManager.CALL_STATE_OFFHOOK) {
                        getRecentOutgoingNumber()?.let { number ->
                            if (number == "SPECIFIC PHONE NUMBER") {
                                redirectToApp()
                            }
                        }
                    }
                    lastCallState = state
                }
            }
        }

        private fun getRecentOutgoingNumber(): String? {
            val projection = arrayOf(CallLog.Calls.NUMBER, CallLog.Calls.TYPE)
            val selection = "${CallLog.Calls.TYPE} = ?"
            val selectionArgs = arrayOf(CallLog.Calls.OUTGOING_TYPE.toString())
            // 按时间倒序取最近一条记录
            val sortOrder = "${CallLog.Calls.DATE} DESC"

            contentResolver.query(
                CallLog.Calls.CONTENT_URI,
                projection,
                selection,
                selectionArgs,
                sortOrder
            )?.use { cursor ->
                if (cursor.moveToFirst()) {
                    val numberIndex = cursor.getColumnIndex(CallLog.Calls.NUMBER)
                    return cursor.getString(numberIndex)
                }
            }
            return null
        }
    }
}

方案二:使用AccessibilityService辅助功能

通过辅助功能监听系统拨号界面的文本输入,实时获取用户输入的号码,可在拨号时触发跳转,但需要用户手动开启辅助功能(属于敏感权限,需向用户明确说明用途)。

步骤:

  1. 创建AccessibilityService:监听拨号应用的文本变化事件
  2. 注册服务:在Manifest中配置服务及权限
  3. 获取并存储号码:提取拨号界面输入的号码,供后续服务使用
  4. 监听通话状态触发跳转:结合PhoneStateListener,在通话接通时匹配号码并跳转

代码示例:

创建CallAccessibilityService:

class CallAccessibilityService : AccessibilityService() {
    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        // 仅监听文本变化事件,且过滤系统拨号应用包名
        if (event.eventType == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED && 
            event.packageName == "com.android.dialer") {
            val inputNumber = event.text.joinToString("")
            // 将号码存储到SharedPreferences,供后续使用
            getSharedPreferences("CallData", MODE_PRIVATE).edit()
                .putString("pending_outgoing_number", inputNumber)
                .apply()
        }
    }

    override fun onInterrupt() {}
}

Manifest注册服务:

<service
    android:name=".CallAccessibilityService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config" />
</service>

辅助功能配置文件(res/xml/accessibility_service_config.xml):

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeViewTextChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_desc"
    android:packageNames="com.android.dialer" />

修改OutgoingCallService获取存储的号码:

@SuppressLint("MissingPermission")
private fun createPhoneStateListener(): PhoneStateListener {
    return object : PhoneStateListener() {
        override fun onCallStateChanged(state: Int, phoneNumber: String?) {
            super.onCallStateChanged(state, phoneNumber)
            if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
                val prefs = getSharedPreferences("CallData", MODE_PRIVATE)
                val targetNumber = prefs.getString("pending_outgoing_number", "")
                if (targetNumber == "SPECIFIC PHONE NUMBER") {
                    redirectToApp()
                }
            }
        }
    }
}

方案对比

方案优点缺点
读取通话记录无需用户额外开启复杂权限只能在通话结束后获取号码,无法实时触发
辅助功能实时获取号码,可提前触发跳转需要用户手动开启辅助功能,敏感权限需说明

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 03:32:14