如何在Angular 14中使用Capacitor获取手机通话记录?
Angular 14 + Capacitor 获取通话记录实现方案
由于Capacitor官方无通话记录相关插件,且原Cordova插件存在稳定性问题,最优方案是自定义Capacitor插件,针对Android/iOS平台特性分别实现原生逻辑,再暴露API给Angular调用。以下是具体实现步骤:
核心思路
- Android:通过系统
ContentResolver读取历史通话记录,需申请对应权限 - iOS:受苹果隐私政策限制,无法获取历史通话记录,仅能通过
CallKit监听实时通话事件(来电/去电)
一、自定义Capacitor插件
先通过Capacitor CLI生成插件基础框架:
npm init @capacitor/plugin call-log-plugin
1. Android端实现(Kotlin)
步骤1:添加权限声明
在android/src/main/AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 可选,用于获取实时通话状态 -->
步骤2:实现通话记录读取逻辑
在插件的CallLogPlugin.kt中编写核心代码:
@CapacitorPlugin(name = "CallLog") class CallLogPlugin : Plugin() { @PluginMethod() fun getCallLogs(call: PluginCall) { // 检查权限 if (!hasPermission(Manifest.permission.READ_CALL_LOG)) { requestPermission(Manifest.permission.READ_CALL_LOG, call) return } val callLogs = mutableListOf<HashMap<String, Any>>() val contentResolver = context.contentResolver val projection = arrayOf( CallLog.Calls.NUMBER, CallLog.Calls.TYPE, CallLog.Calls.DATE, CallLog.Calls.DURATION, CallLog.Calls.NAME ) // 查询通话记录(按时间倒序) contentResolver.query( CallLog.Calls.CONTENT_URI, projection, null, null, "${CallLog.Calls.DATE} DESC" )?.use { cursor -> while (cursor.moveToNext()) { val log = HashMap<String, Any>().apply { put("number", cursor.getString(cursor.getColumnIndexOrThrow(CallLog.Calls.NUMBER))) put("type", cursor.getInt(cursor.getColumnIndexOrThrow(CallLog.Calls.TYPE))) put("date", cursor.getLong(cursor.getColumnIndexOrThrow(CallLog.Calls.DATE))) put("duration", cursor.getLong(cursor.getColumnIndexOrThrow(CallLog.Calls.DURATION))) put("name", cursor.getString(cursor.getColumnIndexOrThrow(CallLog.Calls.NAME)) ?: "未知") } callLogs.add(log) } } call.resolve(JSObject().apply { put("callLogs", callLogs) }) } // 权限申请回调 override fun handleRequestPermissionResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { val call = bridge.getSavedCall(requestCode) ?: return if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { getCallLogs(call) } else { call.reject("权限被拒绝,无法获取通话记录") } } }
2. iOS端实现(Swift)
iOS不允许读取历史通话记录,仅实现实时通话监听:
步骤1:添加权限描述
在ios/Plugin/Info.plist中添加权限说明:
<key>NSCallKitUsageDescription</key> <string>需要获取通话信息以识别来电者身份</string>
步骤2:实现CallKit监听逻辑
在CallLogPlugin.swift中编写监听代码:
import Capacitor import CallKit @objc(CallLogPlugin) public class CallLogPlugin: CAPPlugin, CXCallObserverDelegate { private let callObserver = CXCallObserver() override public func load() { callObserver.setDelegate(self, queue: nil) } @objc func startCallListening(_ call: CAPPluginCall) { call.resolve(["status": "已启动实时通话监听"]) } public func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) { // 监听已接通的通话 if call.hasEnded == false && call.hasConnected == true { if let handle = call.handle as? CXHandle, let phoneNumber = handle.value as? String { // 发送实时通话事件到Angular端 notifyListeners("callReceived", data: [ "number": phoneNumber, "isIncoming": !call.isOutgoing ]) } } } }
二、Angular端集成与调用
1. 定义插件TypeScript接口
在Angular项目中创建call-log-plugin.ts:
import { registerPlugin, PluginListenerHandle } from '@capacitor/core'; export interface CallLog { number: string; type: number; date: number; duration: number; name: string; } export interface CallLogPlugin { getCallLogs(): Promise<{ callLogs: CallLog[] }>; startCallListening(): Promise<{ status: string }>; addListener(eventName: 'callReceived', listenerFunc: (data: { number: string; isIncoming: boolean }) => void): Promise<PluginListenerHandle> & PluginListenerHandle; } const CallLog = registerPlugin<CallLogPlugin>('CallLog'); export default CallLog;
2. 在组件中调用
import { Component, OnInit } from '@angular/core'; import CallLog from './call-log-plugin'; import { Capacitor } from '@capacitor/core'; @Component({ selector: 'app-caller-identify', templateUrl: './caller-identify.component.html' }) export class CallerIdentifyComponent implements OnInit { callLogs: CallLog[] = []; async ngOnInit() { // 监听实时来电(仅iOS有效) CallLog.addListener('callReceived', (data) => { console.log('实时来电号码:', data.number); this.matchCallerInfo(data.number); }); // Android端获取历史通话记录 if (Capacitor.getPlatform() === 'android') { try { const result = await CallLog.getCallLogs(); this.callLogs = result.callLogs; this.callLogs.forEach(log => this.matchCallerInfo(log.number)); } catch (err) { console.error('获取通话记录失败:', err); } } // iOS启动实时监听 if (Capacitor.getPlatform() === 'ios') { await CallLog.startCallListening(); } } private async matchCallerInfo(phoneNumber: string) { // 调用自有数据库接口,传入号码获取来电者信息 // const callerInfo = await this.apiService.getCallerDetail(phoneNumber); // console.log('来电者信息:', callerInfo); } }
三、关键注意事项
- Android:Android 11+对通话记录权限限制更严格,需用户手动授予,且应用需符合Google Play隐私政策
- iOS:苹果禁止读取历史通话记录,若需实现来电弹窗识别,需结合CallKit与后台推送服务(后台运行权限受限)
- 测试:必须在真机上测试,模拟器无法模拟真实通话场景与权限流程
- 隐私合规:需在应用隐私政策中明确说明通话数据的用途,符合各平台隐私要求
内容的提问来源于stack exchange,提问作者user10014893
相关产品推荐
相关产品推荐

