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

如何以编程方式获取通话时长?及按条件结束呼出电话的实现咨询

Hey there! Let's tackle your two Android telephony development questions one by one:

1. 如何通过编程方式获取通话过程中的通话时长

获取通话时长主要有两种实用方案,你可以根据场景选择:

方案一:实时监听通话状态计算时长

通过监听电话状态变化,记录通话接通和结束的时间戳,实时计算时长:

步骤说明:

  • 注册BroadcastReceiver监听android.intent.action.PHONE_STATE动作
  • 当状态变为TelephonyManager.CALL_STATE_OFFHOOK(通话接通)时,记录当前时间
  • 当状态变为TelephonyManager.CALL_STATE_IDLE(通话结束)时,计算时间差得到通话时长
  • 注意:Android 10+ 需要申请READ_PHONE_STATE权限(动态申请);Android 12+ 还需POST_NOTIFICATIONS权限保障广播正常工作

示例代码:

public class CallDurationReceiver extends BroadcastReceiver {
    private long callStartTime = 0;
    private boolean isCallActive = false;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (!intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
            return;
        }

        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

        switch (state) {
            case TelephonyManager.EXTRA_STATE_OFFHOOK:
                // 通话接通,启动计时
                callStartTime = System.currentTimeMillis();
                isCallActive = true;
                break;
            case TelephonyManager.EXTRA_STATE_IDLE:
                if (isCallActive) {
                    // 通话结束,计算时长
                    long durationInMillis = System.currentTimeMillis() - callStartTime;
                    long durationInSeconds = durationInMillis / 1000;
                    Log.d("CallDuration", "当前通话时长:" + durationInSeconds + "秒");
                    isCallActive = false;
                }
                break;
        }
    }
}

方案二:读取系统通话记录

如果需要获取历史通话时长,可以直接读取系统通话记录,需要READ_CALL_LOG权限:

示例代码片段:

ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(
    CallLog.Calls.CONTENT_URI,
    new String[]{CallLog.Calls.DURATION},
    CallLog.Calls.NUMBER + " = ?",
    new String[]{"目标号码"},
    CallLog.Calls.DATE + " DESC"
);

if (cursor != null && cursor.moveToFirst()) {
    long duration = cursor.getLong(cursor.getColumnIndexOrThrow(CallLog.Calls.DURATION));
    Log.d("CallLog", "历史通话时长:" + duration + "秒");
    cursor.close();
}

2. 完善可根据特定条件结束呼出电话的应用

从你给出的代码片段来看,已经在监听电话状态了,接下来我们补充完整逻辑,实现「根据时长、白名单条件结束呼出电话」的功能:

第一步:补充权限与广播配置

除了监听PHONE_STATE,还需要监听NEW_OUTGOING_CALL捕获呼出事件,同时在清单中声明必要权限:

权限声明(AndroidManifest.xml):

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<!-- Android 10+ 额外需要 -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />

注册广播接收器:

<receiver android:name=".CallControlReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE" />
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
    </intent-filter>
</receiver>

第二步:完善接收器核心逻辑

我们需要记录呼出号码、通话开始时间,再结合白名单和时长条件判断是否结束通话:

public class CallControlReceiver extends BroadcastReceiver {
    private long outgoingCallStartTime = 0;
    private String currentOutgoingNumber = "";
    private Context appContext;

    @Override
    public void onReceive(Context context, Intent intent) {
        appContext = context.getApplicationContext();
        String action = intent.getAction();

        if (TelephonyManager.ACTION_NEW_OUTGOING_CALL.equals(action)) {
            // 捕获呼出电话,记录目标号码
            currentOutgoingNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            Log.d("CallControl", "呼出号码:" + currentOutgoingNumber);
        } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

            switch (state) {
                case TelephonyManager.EXTRA_STATE_OFFHOOK:
                    // 呼出电话接通,启动计时检查
                    if (!currentOutgoingNumber.isEmpty()) {
                        outgoingCallStartTime = System.currentTimeMillis();
                        startDurationCheck();
                    }
                    break;
                case TelephonyManager.EXTRA_STATE_IDLE:
                    // 通话结束,重置状态
                    currentOutgoingNumber = "";
                    outgoingCallStartTime = 0;
                    break;
            }
        }
    }

    private void startDurationCheck() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
        long delayMillis = Long.parseLong(prefs.getString("timedelay", "0")) * 1000;

        if (delayMillis <= 0) return;

        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            // 查询白名单,判断是否需要结束通话
            WhitelistDAO whiteListDao = new WhitelistDAO(appContext);
            List<Whitelist> whiteList = whiteListDao.getAllWhitelistItems(); // 假设你有该查询方法
            boolean isInWhitelist = whiteList.stream()
                    .anyMatch(item -> item.getNumber().equals(currentOutgoingNumber));

            if (!isInWhitelist) {
                endCurrentCall();
            }
        }, delayMillis);
    }

    private void endCurrentCall() {
        // 适配不同Android版本的通话结束API
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            TelecomManager telecomManager = (TelecomManager) appContext.getSystemService(Context.TELECOM_SERVICE);
            if (telecomManager != null) {
                try {
                    telecomManager.endCall();
                    Log.d("CallControl", "已自动结束呼出电话");
                } catch (SecurityException e) {
                    Log.e("CallControl", "结束通话失败:权限不足", e);
                }
            }
        } else {
            // Android 9及以下用反射调用废弃的endCall方法
            TelephonyManager telephonyManager = (TelephonyManager) appContext.getSystemService(Context.TELEPHONY_SERVICE);
            try {
                Method endCallMethod = telephonyManager.getClass().getMethod("endCall");
                endCallMethod.invoke(telephonyManager);
                Log.d("CallControl", "已自动结束呼出电话");
            } catch (Exception e) {
                Log.e("CallControl", "结束通话失败", e);
            }
        }
    }
}

关键注意事项:

  • 动态权限申请:Android 6+ 必须动态申请READ_PHONE_STATE、PROCESS_OUTGOING_CALLS、ANSWER_PHONE_CALLS权限
  • 厂商兼容性:部分国产厂商对通话控制功能有额外限制,需在目标设备上测试验证
  • 白名单DAO完善:确保你的WhitelistDAO能正确从数据库读取白名单数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:03:46