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
相关产品推荐
相关产品推荐

