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

Android开发:如何识别呼出电话来自自有应用并标注

解决方案:识别自有APP发起的呼出通话并标注

一、本地存储标记方案(兼容全版本)

核心思路是在发起通话时记录关键信息,后续读取通话日志时匹配这些信息,标记来自自有APP,兼容性覆盖所有Android版本。

步骤1:发起通话时记录标记信息

修改点击通话按钮的逻辑,将拨打的号码和发起时间存入SharedPreferences:

callbtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String phoneno = phoneNo.getText().toString().trim();
        if (phoneno.isEmpty()) {
            Toast.makeText(MainActivity.this, "请输入电话号码", Toast.LENGTH_SHORT).show();
            return;
        }

        // 存储当前呼叫的号码和发起时间
        SharedPreferences sp = getSharedPreferences("AppCallRecords", MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        long callInitiateTime = System.currentTimeMillis();
        editor.putLong("call_" + phoneno, callInitiateTime);
        editor.apply();

        // 发起通话
        Intent i = new Intent(Intent.ACTION_CALL);
        i.setData(Uri.parse("tel:" + phoneno));
        startActivity(i);
    }
});

步骤2:读取通话日志时匹配标记

修改getCallDetails方法,读取本地标记并匹配通话记录:

private String getCallDetails(){
    StringBuffer sb = new StringBuffer();
    Cursor managedCursor = getContentResolver().query(CallLog.Calls.CONTENT_URI, null, null , null, android.provider.CallLog.Calls.DATE + " DESC", null);
    
    int name = managedCursor.getColumnIndex(CallLog.Calls.CACHED_NAME);
    int number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER);
    int type = managedCursor.getColumnIndex(CallLog.Calls.TYPE);
    int date = managedCursor.getColumnIndex(CallLog.Calls.DATE);
    int duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION);
    
    // 获取本地存储的呼叫标记
    SharedPreferences sp = getSharedPreferences("AppCallRecords", MODE_PRIVATE);
    
    sb.append("Call Details:\n\n");
    while(managedCursor.moveToNext()){
        String phnumber = managedCursor.getString(number);
        String cname = managedCursor.getString(name);
        String callType = managedCursor.getString(type);
        long callLogDate = Long.parseLong(managedCursor.getString(date));
        Date callDayTime = new Date(callLogDate);
        SimpleDateFormat newformatter = new SimpleDateFormat("dd-MM-yy || HH:mm:ss:SSS");
        String dateString = newformatter.format(callDayTime);
        String callDuration = managedCursor.getString(duration);
        String dir = null;
        int dircode = Integer.parseInt(callType);
        
        boolean isFromOurApp = false;
        // 只处理呼出通话的匹配
        if (dircode == CallLog.Calls.OUTGOING_TYPE) {
            long initiateTime = sp.getLong("call_" + phnumber, -1);
            // 允许5秒内的时间误差(系统写入通话日志存在延迟)
            if (initiateTime != -1 && Math.abs(callLogDate - initiateTime) < 5000) {
                isFromOurApp = true;
                // 匹配后移除标记,避免重复识别
                sp.edit().remove("call_" + phnumber).apply();
            }
        }
        
        switch (dircode){
            case CallLog.Calls.OUTGOING_TYPE:
                dir = "OUTGOING";
                break;
            case CallLog.Calls.INCOMING_TYPE:
                dir = "INCOMING";
                break;
            case CallLog.Calls.MISSED_TYPE:
                dir = "MISSED";
                break;
        }
        
        sb.append("\nName: "+ cname +"\nPhone Number: "+ phnumber + "\nCall Type: "+ dir);
        if (isFromOurApp) {
            sb.append(" *来自本应用*");
        }
        sb.append("\nCall Date: "+ dateString + "\nCall Duration: "+ callDuration);
        sb.append("\n--------------------------------------");
    }
    managedCursor.close();
    return sb.toString();
}

二、Android 12+ 原生字段方案(更精准)

Android 12(API 31)及以上系统,CallLog新增CallLog.Calls.EXTRA_CALL_ORIGIN字段,可直接获取发起通话的应用包名,无需本地存储匹配。

修改getCallDetails方法

import android.os.Build;

private String getCallDetails(){
    StringBuffer sb = new StringBuffer();
    // 针对Android 12+,添加EXTRA_CALL_ORIGIN字段查询
    String[] projection = {
            CallLog.Calls.CACHED_NAME,
            CallLog.Calls.NUMBER,
            CallLog.Calls.TYPE,
            CallLog.Calls.DATE,
            CallLog.Calls.DURATION,
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? CallLog.Calls.EXTRA_CALL_ORIGIN : ""
    };
    
    Cursor managedCursor = getContentResolver().query(
            CallLog.Calls.CONTENT_URI, 
            projection, 
            null , 
            null, 
            android.provider.CallLog.Calls.DATE + " DESC", 
            null
    );
    
    int name = managedCursor.getColumnIndex(CallLog.Calls.CACHED_NAME);
    int number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER);
    int type = managedCursor.getColumnIndex(CallLog.Calls.TYPE);
    int date = managedCursor.getColumnIndex(CallLog.Calls.DATE);
    int duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION);
    int callOriginIndex = -1;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        callOriginIndex = managedCursor.getColumnIndex(CallLog.Calls.EXTRA_CALL_ORIGIN);
    }
    
    sb.append("Call Details:\n\n");
    while(managedCursor.moveToNext()){
        String phnumber = managedCursor.getString(number);
        String cname = managedCursor.getString(name);
        String callType = managedCursor.getString(type);
        long callLogDate = Long.parseLong(managedCursor.getString(date));
        Date callDayTime = new Date(callLogDate);
        SimpleDateFormat newformatter = new SimpleDateFormat("dd-MM-yy || HH:mm:ss:SSS");
        String dateString = newformatter.format(callDayTime);
        String callDuration = managedCursor.getString(duration);
        String dir = null;
        int dircode = Integer.parseInt(callType);
        
        boolean isFromOurApp = false;
        if (dircode == CallLog.Calls.OUTGOING_TYPE) {
            // Android 12+ 直接读取发起应用包名
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && callOriginIndex != -1) {
                String originPackage = managedCursor.getString(callOriginIndex);
                if (getPackageName().equals(originPackage)) {
                    isFromOurApp = true;
                }
            }
        }
        
        switch (dircode){
            case CallLog.Calls.OUTGOING_TYPE:
                dir = "OUTGOING";
                break;
            case CallLog.Calls.INCOMING_TYPE:
                dir = "INCOMING";
                break;
            case CallLog.Calls.MISSED_TYPE:
                dir = "MISSED";
                break;
        }
        
        sb.append("\nName: "+ cname +"\nPhone Number: "+ phnumber + "\nCall Type: "+ dir);
        if (isFromOurApp) {
            sb.append(" *来自本应用*");
        }
        sb.append("\nCall Date: "+ dateString + "\nCall Duration: "+ callDuration);
        sb.append("\n--------------------------------------");
    }
    managedCursor.close();
    return sb.toString();
}

三、通话状态监听(实现通话结束返回APP)

通过广播接收器监听通话状态,在通话结束时自动返回自有APP,同时可辅助标记通话来源:

1. 创建通话状态广播接收器

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;

public class CallStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        
        // 通话结束时返回APP
        if (TelephonyManager.EXTRA_STATE_IDLE.equals(state)) {
            Intent appIntent = new Intent(context, MainActivity.class);
            appIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            context.startActivity(appIntent);
        }
    }
}

2. 在MainActivity中注册广播

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ... 现有代码
    
    // 注册通话状态广播
    IntentFilter filter = new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
    registerReceiver(new CallStateReceiver(), filter);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 注销广播避免内存泄漏
    unregisterReceiver(new CallStateReceiver());
}

注意:需要动态申请READ_PHONE_STATE权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 18:24:50