需求
在 app 中点击按钮拨打电话,电话接通后进行录音,电话挂断以后可保存录音文件并获取通话时长。
功能实现
本次使用 HBuilderX 创建的 uni-app 项目进行实现。
拨打电话
拨打电话有两种方式,一是 plus.device.dial() , 二是 href='tel:xxxxxxx'.
plus.device.dial()
这是 HTML5+的一个API,具体说明如图:
href='tel:xxxxxxx'
- 使用 a 标签
<a href="tel:xxxxxxx">拨打电话</a>
- js 操作
function callPhone() {
let phoneNumber = document.getElementById('tel').value;
window.location.href = 'tel:' + phoneNumber;
}
录音
本次并没有实现通话录音功能,了解到相关资料如下:
- 使用 android.media.MediaRecorder
- 由于隐私保护等问题,只能实现单边录音。(没有深入研究,是否如此待考证。)
获取通话记录
获取通话记录需要相关的权限,而且是动态权限。 在 manifest.json 中勾选:
动态权限:
plus.android.requestPermissions(
['android.permission.READ_CALL_LOG', 'android.permission.WRITE_CALL_LOG',
'android.permission.READ_SMS'
],
function(e) {
if (e.deniedAlways.length > 0) {
//权限被永久拒绝
// 弹出提示框解释为何需要定位权限,引导用户打开设置页面开启
console.log('Always Denied!!! ' + e.deniedAlways.toString());
}
if (e.deniedPresent.length > 0) {
//权限被临时拒绝
// 弹出提示框解释为何需要定位权限,可再次调用plus.android.requestPermissions申请权限
console.log('Present Denied!!! ' + e.deniedPresent.toString());
}
if (e.granted.length > 0) {
//权限被允许
//调用依赖获取定位权限的代码
console.log('Granted!!! ' + e.granted.toString());
}
},
function(e) {
console.log('Request Permissions error:' + JSON.stringify(e));
}
)
获取通话记录:
var CallLog = plus.android.importClass('android.provider.CallLog');
var Activity = plus.android.runtimeMainActivity();
var ContentResolver = plus.android.importClass('android.content.ContentResolver');
var resolver = Activity.getContentResolver();
plus.android.importClass(resolver);
var String = plus.android.importClass("java.lang.String");
var cs = resolver.query(CallLog.Calls.CONTENT_URI, null, null, null, CallLog.Calls
.DEFAULT_SORT_ORDER);
plus.android.importClass(cs);
要实现电话挂断后自动获取通话记录,那就得监听通话状态,此处用到了 TelephonyManager,而且需要勾选以下权限:
遇到的问题
- 监听状态的服务启动以后,在 onReceive 方法中, this 只能获取到 data 中定义的内容,对于 methods 中定义的 getCalllog 方法则会提示未定义,在 data 中定义一个变量然后在 getCalllog 方法中赋值。
- 报 TYPE 错误。这个应该是由于权限问题引发的错误,打包或使用自定义基座运行可解决。
完整代码
<template>
<view class="content">
<view class="box">
<input v-model="telNumber" type="tel" value="" />
</view>
<view class="box">
<button type="primary" @click="dial">拨号</button>
</view>
<div>
<h4>content======输出打印=======</h4>
<div>{{phoneState}}</div>
<div v-for="(item,index) in content">{{item.callTime + '与' + item.mobile + '通话' + item.talkTime + '秒'}}
</div>
</div>
</view>
</template>
<script>
export default {
data() {
return {
telNumber: 'xxxxxx',
content: [],
phoneState: '',
getCallLogFn: null,
}
},
onLoad() {
this.listenToCall();
},
methods: {
// 拨号
dial: function() {
// #ifdef APP-PLUS
this.callTime = new Date();
plus.device.dial(this.telNumber, false);
// #endif
},
listenToCall() {
let _this = this;
_this.getCalllog();
let maintest = plus.android.runtimeMainActivity();
let Contexttest = plus.android.importClass("android.content.Context");
let telephonyManager = plus.android.importClass("android.telephony.TelephonyManager");
let telManager = plus.android.runtimeMainActivity().getSystemService(Contexttest.TELEPHONY_SERVICE);
/* let MediaRecorder = plus.android.importClass('android.media.MediaRecorder');
let recorder = new MediaRecorder(); */
let receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: function(Contexttest, intent) {
plus.android.importClass(intent);
// console.log(intent.getAction());
// let telephonyManager = plus.android.importClass("android.telephony.TelephonyManager");
// let telephonyManager=plus.android.runtimeMainActivity().getSystemService(Contexttest.TELEPHONY_SERVICE);
let phonetype = telManager.getCallState();
let phoneNumber = intent.getStringExtra(telephonyManager.EXTRA_INCOMING_NUMBER);
console.log("phoneType:" + phonetype); //电话状态 0->空闲状态 1->振铃状态 2->通话存在
if (phonetype === 0) {
try {
_this.phoneState = '空闲状态';
/* recorder.stop();
recorder.reset();
recorder.release();
recorder = null; */
_this.getCallLogFn();
} catch (e) {
console.log(e);
}
}
/* else if(phonetype === 2) {
try {
console.log('try')
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setOutputFile("_doc/audio/record.amr");
recorder.prepare();
recorder.start();
} catch (e) {
console.log(e.message);
}
} */
}
});
let IntentFilter = plus.android.importClass('android.content.IntentFilter');
let filter = new IntentFilter();
filter.addAction(telephonyManager.ACTION_PHONE_STATE_CHANGED);
maintest.registerReceiver(receiver, filter);
},
getCalllog() {
this.getCallLogFn = () => {
this.content = [];
//#ifdef APP-PLUS
// 权限处理
plus.android.requestPermissions(
['android.permission.READ_CALL_LOG', 'android.permission.WRITE_CALL_LOG',
'android.permission.READ_SMS'
],
function(e) {
if (e.deniedAlways.length > 0) {
//权限被永久拒绝
// 弹出提示框解释为何需要定位权限,引导用户打开设置页面开启
console.log('Always Denied!!! ' + e.deniedAlways.toString());
}
if (e.deniedPresent.length > 0) {
//权限被临时拒绝
// 弹出提示框解释为何需要定位权限,可再次调用plus.android.requestPermissions申请权限
console.log('Present Denied!!! ' + e.deniedPresent.toString());
}
if (e.granted.length > 0) {
//权限被允许
//调用依赖获取定位权限的代码
console.log('Granted!!! ' + e.granted.toString());
}
},
function(e) {
console.log('Request Permissions error:' + JSON.stringify(e));
}
)
// 获取通话记录的主体代码 顺序不能够乱
var CallLog = plus.android.importClass('android.provider.CallLog');
var Activity = plus.android.runtimeMainActivity();
var ContentResolver = plus.android.importClass('android.content.ContentResolver');
var resolver = Activity.getContentResolver();
plus.android.importClass(resolver);
var String = plus.android.importClass("java.lang.String");
var cs = resolver.query(CallLog.Calls.CONTENT_URI, null, null, null, CallLog.Calls.DEFAULT_SORT_ORDER);
plus.android.importClass(cs);
var count = 0; // 记录多少条 用于处理循环跳出
while (cs.moveToNext()) {
count++;
//号码
var number = cs.getString(cs.getColumnIndex(CallLog.Calls.NUMBER));
//呼叫类型
var type;
switch (parseInt(cs.getString(cs.getColumnIndex(CallLog.Calls.TYPE))))
// 判断通话类型
{
case CallLog.Calls.INCOMING_TYPE:
type = "呼入";
break;
case CallLog.Calls.OUTGOING_TYPE:
type = "呼出";
break;
case CallLog.Calls.MISSED_TYPE:
type = "未接";
break;
default:
type = "挂断";
break;
}
// 获取时间
var date = new Date(parseInt(
cs.getString(cs.getColumnIndexOrThrow(CallLog.Calls.DATE))));
// 联系人
var Name_Col = cs.getColumnIndexOrThrow(CallLog.Calls.CACHED_NAME);
var name = cs.getString(Name_Col);
// 号码归属地 返回:北京 联通
var numberLocation = cs.getString(
cs.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION)
);
//通话时间,单位:s
var Duration_Col = cs.getColumnIndexOrThrow(CallLog.Calls.DURATION);
var duration = cs.getString(Duration_Col);
// 存入数组
this.content.push({
name: name, // 联系人的姓名
mobile: number, // 联系人电话
numberLocation: numberLocation, // 号码的归属地
callTime: this.formatDate(date), // 呼入或呼出时间
talkTime: duration, // 通话时长
type: type
});
if (count > 0) {
break;
}
}
}
//#endif
},
// 毫秒转日期
formatDate(now) {
const date = new Date(now)
var y = date.getFullYear() // 年份
var m = date.getMonth() + 1 // 月份,注意:js里的月要加1
var d = date.getDate() // 日
var h = date.getHours() // 小时
var min = date.getMinutes() // 分钟
var s = date.getSeconds() // 秒
return y + '-' + m + '-' + d + ' ' + h + ':' + min + ':' + s
},
}
}
</script>
<style>
.box {
width: 100%;
margin-top: 50rpx;
}
</style>
其他
生成证书
登录 dev.dcloud.net.cn/ ,显示应用列表:
点击应用名称进入应用详情页,选择“应用证书管理”可生成证书:
证书生成很快的,不过生成后需要刷新才会显示“证书已生成”。
自定义基座
菜单栏选择 “运行” -> “运行到手机或模拟器” -> “制作自定义调试基座”,出现弹窗以后填写证书相关信息,选择“传统打包”,点击“打包”就可以了。
调试
手机通过 USB 连接电脑以后,点击“运行” -> “运行到手机或模拟器”,选择“自定义基座”,再选择运行设备就好了。由于手头没有安卓机就不截图了。
参考
别人写好的插件:获取通话记录 发短信 拨号
监听电话状态