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_STATEPROCESS_OUTGOING_CALLSANSWER_PHONE_CALLS权限
  • 厂商兼容性:部分国产厂商对通话控制功能有额外限制,需在目标设备上测试验证
  • 白名单DAO完善:确保你的WhitelistDAO能正确从数据库读取白名单数据

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

火山引擎 最新活动