更新记录

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: 通话记录获取失败怎么办?

可能原因:

  1. 没有 READ_CALL_LOG 权限
  2. 通话记录还未生成(需要延时获取)

解决方案:

  1. 确保已调用 requestAllPermission() 并授予权限
  2. 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+ 路径问题
  • ✅ 支持按钮自定义样式(宽度、高度、背景色、圆角)

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.CALL_PHONE android.permission.READ_PHONE_STATE

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。