更新记录
1.0.0(2025-12-19)
- 初始版本
- 支持拨打电话、通话监听、录音等功能
- 支持双卡设备
- 完整的 API 接口
平台兼容性
uni-app(4.61)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | √ | √ | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - |
电话通话 UTS 插件使用文档
简介
电话通话 UTS 插件是一个功能完整的电话通话解决方案,基于原生 Android 电话服务开发,使用 UTS 语法实现。插件提供了拨打电话、通话监听、通话录音、通话记录管理等完整功能,支持单卡/双卡设备,可高度自定义通话界面样式。
适用场景:
- 客服系统
- 电话营销工具
- 通话管理应用
- 企业通讯应用
功能特性
- ✅ 拨打电话:支持单卡/双卡设备,可指定SIM卡拨号
- ✅ 通话监听:实时监听通话状态(响铃、接听、挂断、通话中)
- ✅ 通话录音:自动录音通话内容(Android 9及以下支持通话录音,高版本支持普通录音)
- ✅ 通话记录:获取、删除通话记录,监听记录变化
- ✅ 界面自定义:高度自定义通话界面样式(背景、按钮、文字等)
- ✅ 权限管理:一键请求所有必要权限
- ✅ 默认应用:支持设置为系统默认电话应用
快速开始
1. 安装插件
将 phonecall-uts 目录复制到项目的 uni_modules 目录下。
2. 导入插件
// #ifdef APP-PLUS
import { getPhoneCallUtsClient } from '@/uni_modules/phonecall-uts'
// #endif
const phoneCallClient = getPhoneCallUtsClient()
3. 基础使用(5步完成)
// 步骤1:请求权限(应用启动时调用)
phoneCallClient.requestAllPermission()
// 步骤2:设置通话页面配置(包含录音路径)
// #ifdef APP-PLUS
const recordPath = plus.io.convertLocalFileSystemURL("_doc/CallRecords")
// #endif
phoneCallClient.setCallPageConfig({
recordPath: recordPath, // 录音文件保存路径(必填)
bgColor: "#2a78d5", // 背景颜色
showTxtColor: "#FFFFFF", // 电话号码颜色
pickUpBgColor: "#4CAF50", // 接听按钮背景色
hangUpBgColor: "#F44336" // 挂断按钮背景色
})
// 步骤3:开启通话监听(页面加载时)
phoneCallClient.onCallListener((result) => {
const state = result.callState
if (state === "ringing") {
console.log("电话响铃中")
} else if (state === "pickup") {
console.log("电话已接听")
// 开始录音
phoneCallClient.startRecord()
} else if (state === "calling") {
// 通话中,持续回调,包含通话时长(毫秒)
const time = result.time || 0
const minutes = Math.floor(time / 60)
const seconds = time % 60
console.log(`通话中: ${minutes}:${seconds.toString().padStart(2, '0')}`)
} else if (state === "hangup") {
console.log("电话已挂断")
// 停止录音
phoneCallClient.stopRecord((result) => {
if (result.code === 0) {
console.log("录音文件:", result.data.fileName)
console.log("文件路径:", result.data.filePath)
}
})
} else if (state === "recordsDidChange") {
console.log("通话记录发生变化")
}
})
// 步骤4:拨打电话
phoneCallClient.makingCalls("***", "张三")
// 步骤5:页面卸载时移除监听
onUnload(() => {
phoneCallClient.removeCallListener()
})
API 文档
初始化
getPhoneCallUtsClient()
获取插件客户端实例。
// #ifdef APP-PLUS
import { getPhoneCallUtsClient } from '@/uni_modules/phonecall-uts'
// #endif
const phoneCallClient = getPhoneCallUtsClient()
权限管理
requestAllPermission()
请求插件所需的所有权限。建议在应用启动时调用。
phoneCallClient.requestAllPermission()
所需权限:
- 拨打电话权限
- 读取电话状态权限
- 读取通话记录权限
- 录音权限
- 悬浮窗权限
- 存储权限(Android 10以下)
通话页面配置
setCallPageConfig(config)
设置通话页面的显示配置和录音路径。使用录音功能前必须调用此方法设置录音路径。
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
recordPath |
string | ✅ | 录音文件保存路径(必填) |
isRealPhone |
boolean | ❌ | 是否显示真实电话号码,false时显示为密文(如:138****8000) |
背景配置(优先级:bgImg > bgGradient > bgColor):
| 参数 | 类型 | 说明 |
|---|---|---|
bgImg |
string | 背景图片路径 |
bgColor |
string | 背景颜色(十六进制,如 "#FF0000") |
bgGradient |
object | 背景渐变配置 |
bgGradient.startColor |
string | 起始颜色 |
bgGradient.endColor |
string | 结束颜色 |
bgGradient.direction |
string | 渐变方向:"vertical"(垂直)或 "horizontal"(水平) |
标签配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
callInlabel |
string | "来电号码" | 来电标签文字 |
callOutlabel |
string | "呼叫号码" | 去电标签文字 |
labelColor |
string | - | 标签文字颜色(十六进制) |
labelSize |
number | 18 | 标签文字大小(sp) |
labelPosition |
string | "bottom" | 标签位置:"top" 或 "bottom" |
电话号码显示配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
showTxtColor |
string | - | 电话号码文字颜色(十六进制) |
showTxtSize |
number | 32 | 电话号码文字大小(sp) |
showTxtStyle |
string | "normal" | 文字样式:"normal"、"bold"、"italic"、"bold_italic" |
phoneNumberPosition |
string | "center" | 电话号码垂直位置:"top"、"center" 或 "bottom" |
接听按钮配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
pickUpText |
string | "接 听" | 接听按钮文字 |
pickUpColor |
string | - | 接听按钮文字颜色(十六进制) |
pickUpWidth |
number | 70 | 接听按钮容器宽度(dp) |
pickUpHeight |
number | 70 | 接听按钮容器高度(dp) |
pickUpTextSize |
number | 20 | 接听按钮文字大小(sp) |
pickUpBgColor |
string | - | 接听按钮背景色(十六进制) |
pickUpCornerRadius |
number | - | 接听按钮圆角(dp) |
pickUpVisible |
boolean | true | 接听按钮是否可见(去电时自动隐藏) |
挂断按钮配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
hangUpText |
string | "挂 断" | 挂断按钮文字 |
hangUpColor |
string | - | 挂断按钮文字颜色(十六进制) |
hangUpWidth |
number | 70 | 挂断按钮容器宽度(dp) |
hangUpHeight |
number | 70 | 挂断按钮容器高度(dp) |
hangUpTextSize |
number | 20 | 挂断按钮文字大小(sp) |
hangUpBgColor |
string | - | 挂断按钮背景色(十六进制) |
hangUpCornerRadius |
number | - | 挂断按钮圆角(dp) |
免提按钮配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
pickFreeText |
string | "免 提" | 免提按钮文字 |
pickFreeColor |
string | - | 免提按钮文字颜色(十六进制) |
pickFreeWidth |
number | 70 | 免提按钮容器宽度(dp) |
pickFreeHeight |
number | 70 | 免提按钮容器高度(dp) |
pickFreeTextSize |
number | 20 | 免提按钮文字大小(sp) |
pickFreeBgColor |
string | - | 免提按钮背景色(十六进制) |
pickFreeCornerRadius |
number | - | 免提按钮圆角(dp) |
pickFreeVisible |
boolean | true | 免提按钮是否可见 |
按钮布局配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
buttonSpacing |
number | 50 | 按钮之间的间距(dp) |
buttonPosition |
string | "center" | 按钮区域位置:"center" 或 "bottom" |
buttonBottomMargin |
number | - | 按钮距离底部的距离(dp),仅在 buttonPosition 为 "bottom" 时生效 |
通话时长显示配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
callingTimeVisible |
boolean | true | 通话时长是否可见 |
callingTimeColor |
string | - | 通话时长文字颜色(十六进制) |
callingTimeSize |
number | 18 | 通话时长文字大小(sp) |
callingTimeFormat |
string | "通话中:{time}" | 通话时长格式,{time} 会被替换为实际时长(MM:SS) |
配置示例:
// 简洁配置(推荐)
phoneCallClient.setCallPageConfig({
recordPath: recordPath, // 必填
bgColor: "#2a78d5",
showTxtColor: "#FFFFFF",
pickUpBgColor: "#4CAF50",
hangUpBgColor: "#F44336"
})
// 完整配置(自定义所有样式)
phoneCallClient.setCallPageConfig({
// 基础配置
recordPath: recordPath,
isRealPhone: false, // 显示为密文
// 背景配置
bgColor: "#2a78d5",
// 或使用渐变
// bgGradient: {
// startColor: "#1E88E5",
// endColor: "#1976D2",
// direction: "vertical"
// },
// 或使用背景图片
// bgImg: "/static/bg.jpg",
// 标签配置
callInlabel: "来电",
callOutlabel: "正在呼叫",
labelColor: "#FFFFFF",
labelSize: 18,
labelPosition: "bottom",
// 电话号码配置
showTxtColor: "#FFFFFF",
showTxtSize: 32,
showTxtStyle: "bold",
phoneNumberPosition: "center",
// 接听按钮
pickUpText: "接听",
pickUpColor: "#FFFFFF",
pickUpWidth: 72,
pickUpHeight: 72,
pickUpBgColor: "#4F4F4F",
pickUpCornerRadius: 36,
pickUpTextSize: 16,
// 挂断按钮
hangUpText: "挂断",
hangUpColor: "#FFFFFF",
hangUpWidth: 72,
hangUpHeight: 72,
hangUpBgColor: "#F44336",
hangUpCornerRadius: 36,
hangUpTextSize: 16,
// 免提按钮
pickFreeText: "免提",
pickFreeColor: "#FFFFFF",
pickFreeWidth: 72,
pickFreeHeight: 72,
pickFreeBgColor: "#777777",
pickFreeCornerRadius: 36,
pickFreeTextSize: 16,
// 按钮布局
buttonSpacing: 48,
buttonPosition: "bottom",
buttonBottomMargin: 80,
// 通话时长
callingTimeVisible: true,
callingTimeColor: "#FFFFFF",
callingTimeSize: 16,
callingTimeFormat: "通话时长 {time}"
})
通话监听
onCallListener(callback)
开始监听通话状态变化。推荐使用此方法,因为方法名以 on 开头,回调会自动保持活跃,不会被垃圾回收。
参数:
callback(function): 回调函数
回调数据:
{
callState: "ringing" | "pickup" | "hangup" | "calling" | "recordsDidChange",
time?: number // 通话时长(毫秒),仅在 callState 为 "calling" 时存在
}
通话状态说明:
"ringing": 电话响铃中"pickup": 电话已接听"hangup": 电话已挂断"calling": 通话中(会持续回调,包含time字段,单位为毫秒)"recordsDidChange": 通话记录发生变化
使用示例:
phoneCallClient.onCallListener((result) => {
const state = result.callState
if (state === "ringing") {
console.log("电话响铃中")
} else if (state === "pickup") {
console.log("电话已接听")
// 开始录音
phoneCallClient.startRecord()
} else if (state === "calling") {
// 通话中,持续回调
const time = result.time || 0 // 毫秒
const minutes = Math.floor(time / 60)
const seconds = time % 60
console.log(`通话中: ${minutes}:${seconds.toString().padStart(2, '0')}`)
} else if (state === "hangup") {
console.log("电话已挂断")
// 停止录音
phoneCallClient.stopRecord((result) => {
if (result.code === 0) {
console.log("录音文件:", result.data.fileName)
}
})
} else if (state === "recordsDidChange") {
console.log("通话记录发生变化")
// 可以在这里刷新通话记录列表
}
})
removeCallListener()
停止监听通话状态变化。页面卸载时必须调用。
onUnload(() => {
phoneCallClient.removeCallListener()
})
listeningServiceIsRuning()
检查通话监听服务是否正在运行。
返回值: boolean - true 表示运行中,false 表示未运行
const isRunning = phoneCallClient.listeningServiceIsRuning()
console.log("监听服务运行中:", isRunning)
拨打电话
makingCalls(phoneNumber, displayName?)
拨打电话,使用默认SIM卡。
参数:
phoneNumber(string, 必填): 要拨打的电话号码displayName(string, 可选): 通话界面显示的名称,不传则显示电话号码
// 简单拨打
phoneCallClient.makingCalls("***")
// 带显示名称
phoneCallClient.makingCalls("***", "张三")
// 显示为密文
const displayName = phone.substring(0, 3) + "****" + phone.substring(7, 11)
phoneCallClient.makingCalls("***", displayName)
makingCallsWithSim(phoneNumber, displayName, simCardIndex)
使用指定的SIM卡拨打电话。
参数:
phoneNumber(string, 必填): 要拨打的电话号码displayName(string | null, 可选): 通话界面显示的名称simCardIndex(number, 必填): SIM卡索引,1 或 2(1表示卡1,2表示卡2)
注意:
- 单卡设备不要使用此方法,应使用
makingCalls - 如果指定的卡槽没有卡,将使用另一张卡拨号
// 检查是否双卡
const isMultiSim = phoneCallClient.isMultiSim()
if (isMultiSim) {
// 使用SIM卡2拨打电话
phoneCallClient.makingCallsWithSim("***", "张三", 2)
} else {
// 单卡设备使用普通拨号
phoneCallClient.makingCalls("***", "张三")
}
isMultiSim()
检查设备是否支持双卡。
返回值: boolean - true 表示双卡,false 表示单卡
const isMultiSim = phoneCallClient.isMultiSim()
console.log("是否双卡:", isMultiSim)
通话录音
startRecord()
开始录音通话。
注意:
- 需要先调用
setCallPageConfig设置录音保存路径 - 需要
RECORD_AUDIO权限 - 通话录音只在 Android 9 及以下有效,高版本只能做普通录音使用
// 接听时开始录音
phoneCallClient.onCallListener((result) => {
if (result.callState === "pickup") {
phoneCallClient.startRecord()
}
})
stopRecord(callback)
停止录音并返回录音文件信息。
参数:
callback(function): 回调函数
回调数据:
{
code: number, // 0-成功,-1-失败
message: string, // 结果描述
data: {
fileName: string, // 录音文件名(如:"record_20231218120000.3gp")
filePath: string // 录音文件完整路径(优先使用此路径)
}
}
使用示例:
phoneCallClient.stopRecord((result) => {
if (result.code === 0) {
const fileName = result.data.fileName
const filePath = result.data.filePath // 完整路径,优先使用此路径
console.log("录音完成,文件:", fileName)
console.log("文件路径:", filePath)
// 使用 filePath 进行文件操作
// #ifdef APP-PLUS
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
console.log("文件存在:", entry.fullPath)
}, (error) => {
console.log("文件不存在:", error)
})
// #endif
} else {
console.log("录音失败:", result.message)
}
})
重要提示:
- 录音文件保存为 3GP 格式
- Android 10+ 系统可能会将录音文件保存到应用私有目录,而不是外部存储路径
- 建议使用返回的
filePath而不是自行拼接路径
通话记录
getCallRecords(param, callback)
获取通话记录列表。
参数:
param(object): 查询参数type(number, 可选): 记录类型,0-全部记录,1-仅未接记录,默认为 0
callback(function): 回调函数
回调数据:
{
code: number, // 0-成功,-1-失败
message: string, // 结果描述
data: {
count: number, // 记录数量
records: [ // 通话记录数组
{
name: string, // 联系人名称
number: string, // 电话号码
duration: string, // 通话时长
type: string, // 通话类型("打入"、"打出"、"未接")
date: string, // 日期("今天"、"昨天"或"yyyy-MM-dd")
time: string // 时间("HH:mm:ss")
}
]
}
}
使用示例:
// 获取全部通话记录
phoneCallClient.getCallRecords({
type: 0
}, (result) => {
if (result.code === 0) {
const count = result.data.count
const records = result.data.records
console.log(`获取到 ${count} 条通话记录`)
records.forEach(record => {
console.log(`${record.name} - ${record.number} - ${record.type} - ${record.date} ${record.time}`)
})
} else {
console.log("获取通话记录失败:", result.message)
}
})
// 仅获取未接记录
phoneCallClient.getCallRecords({
type: 1
}, (result) => {
if (result.code === 0) {
const records = result.data.records
console.log(`未接记录: ${records.length} 条`)
}
})
deleteCallRecords()
删除所有通话记录。
注意: 此操作会删除所有通话记录,请谨慎使用。为确保调用API时通话记录已生成,建议添加延时操作。
// 删除通话记录(建议在 recordsDidChange 回调后延时调用)
phoneCallClient.onCallListener((result) => {
if (result.callState === "recordsDidChange") {
// 为确保调用Api时通话记录已生成, 必须添加延时操作
setTimeout(() => {
phoneCallClient.deleteCallRecords()
console.log("已删除通话记录")
}, 500)
}
})
默认电话应用
setToTheDefaultCallApplication()
将当前应用设置为系统默认电话应用。此操作会弹出系统设置页面,需要用户手动确认。
phoneCallClient.setToTheDefaultCallApplication()
uni.showToast({
title: "请在弹出的页面中确认",
icon: 'none',
duration: 3000
})
isDefaultCallApplication()
检查当前应用是否为系统默认电话应用。
返回值: boolean - true 表示是默认应用,false 表示不是
const isDefault = phoneCallClient.isDefaultCallApplication()
console.log("是否为默认电话应用:", isDefault)
openDefaultAppSettings()
打开系统设置页面,允许用户修改默认电话应用。此方法会直接跳转到系统设置页面,比 setToTheDefaultCallApplication 更灵活。
phoneCallClient.openDefaultAppSettings()
uni.showToast({
title: "请在设置中选择默认电话应用",
icon: 'none',
duration: 3000
})
SIM卡相关
getDefaultSim(callback)
获取当前默认SIM卡的索引。
参数:
callback(function): 回调函数
回调数据:
{
code: number, // 0-成功,-1-失败
message: string, // 结果描述
data: {
simIndex: number // SIM卡索引(0或1),-1表示获取失败
}
}
phoneCallClient.getDefaultSim((result) => {
if (result.code === 0) {
const simIndex = result.data.simIndex
console.log("默认SIM卡索引:", simIndex) // 0 或 1
}
})
isDefaultSIMEnabled(callback)
检查默认SIM卡是否已启用。
参数:
callback(function): 回调函数
回调数据:
{
code: number, // 0-成功,-1-失败
message: string, // 结果描述
data: {
enabled: boolean // true-已启用,false-未启用
}
}
phoneCallClient.isDefaultSIMEnabled((result) => {
if (result.code === 0) {
const enabled = result.data.enabled
console.log("默认SIM卡是否启用:", enabled)
}
})
重要注意事项
1. 初始化顺序
使用插件前,必须按以下顺序初始化:
// 1. 请求权限(应用启动时)
phoneCallClient.requestAllPermission()
// 2. 设置配置(包含录音路径)
phoneCallClient.setCallPageConfig({
recordPath: recordPath // 必填
})
// 3. 开启监听(页面加载时)
phoneCallClient.onCallListener((result) => {
// 处理通话状态
})
// 4. 页面卸载时移除监听
onUnload(() => {
phoneCallClient.removeCallListener()
})
2. 录音路径设置
录音功能必须设置录音路径,否则无法录音:
// #ifdef APP-PLUS
const recordPath = plus.io.convertLocalFileSystemURL("_doc/CallRecords")
// #endif
phoneCallClient.setCallPageConfig({
recordPath: recordPath // 必填
})
3. 录音文件路径
Android 10+ 系统可能会将录音文件保存到应用私有目录,建议使用 stopRecord 返回的 filePath 而不是自行拼接路径。
phoneCallClient.stopRecord((result) => {
if (result.code === 0) {
// 使用返回的完整路径
const filePath = result.data.filePath
// 不要自行拼接路径
// const filePath = recordPath + '/' + result.data.fileName
}
})
4. 通话录音限制
- Android 9 及以下:支持通话录音(录音通话双方的声音)
- Android 10 及以上:只能做普通录音使用(无法录音通话双方的声音)
5. 双卡设备使用
- 使用
isMultiSim()检查设备是否支持双卡 - 单卡设备不要使用
makingCallsWithSim,应使用makingCalls - 如果指定的卡槽没有卡,将使用另一张卡拨号
6. 悬浮窗权限
开启监听服务需要悬浮窗权限(SYSTEM_ALERT_WINDOW),requestAllPermission() 会自动请求此权限,但部分设备可能需要用户手动在系统设置中开启。
常见问题
Q1: 录音文件找不到怎么办?
原因: Android 10+ 系统可能会将录音文件保存到应用私有目录,而不是外部存储路径。
解决方案: 使用 stopRecord 返回的 filePath 字段,而不是自行拼接路径。
phoneCallClient.stopRecord((result) => {
if (result.code === 0) {
// 使用返回的完整路径
const filePath = result.data.filePath
}
})
Q2: 如何检查插件是否正常工作?
// 1. 检查监听服务状态
const isRunning = phoneCallClient.listeningServiceIsRuning()
console.log("监听服务运行中:", isRunning)
// 2. 检查是否为默认电话应用
const isDefault = phoneCallClient.isDefaultCallApplication()
console.log("是否为默认电话应用:", isDefault)
// 3. 检查是否双卡
const isMultiSim = phoneCallClient.isMultiSim()
console.log("是否双卡:", isMultiSim)
Q3: 通话记录获取失败怎么办?
可能原因:
- 没有
READ_CALL_LOG权限 - 通话记录还未生成(需要延时获取)
解决方案:
- 确保已调用
requestAllPermission()并授予权限 - 在
recordsDidChange回调后延时获取:
phoneCallClient.onCallListener((result) => {
if (result.callState === "recordsDidChange") {
// 为确保调用Api时通话记录已生成, 必须添加延时操作
setTimeout(() => {
phoneCallClient.getCallRecords({ type: 0 }, (result) => {
if (result.code === 0) {
console.log("获取到", result.data.count, "条记录")
}
})
}, 500)
}
})
兼容性说明
- Android 版本:支持 Android 5.0 (API 21) 及以上版本
- uni-app 版本: UTS 插件的版本
- HBuilderX 版本:需要 HBuilderX 3.6.8 及以上版本
- 双卡支持:仅在支持双卡的设备上有效,单卡设备调用双卡相关 API 会返回默认值
- 通话录音:Android 9 及以下支持通话录音,Android 10+ 仅支持普通录音
更新日志
v1.0.0 (2025-12-18)
- ✅ 支持拨打电话、通话监听、录音等功能
- ✅ 支持双卡设备
- ✅ 完整的 API 接口,与原始代码保持一致
- ✅ 支持自定义通话界面样式
- ✅ 新增
onCallListener方法,解决回调函数释放问题 - ✅ 录音功能返回完整文件路径,解决 Android 10+ 路径问题
- ✅ 支持按钮自定义样式(宽度、高度、背景色、圆角)

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 271
赞赏 2
下载 12411435
赞赏 1829
赞赏
京公网安备:11010802035340号