更新记录
1.5.0(2024-06-24)
本次主要更新: 1.增加 自定义拨号界面UI 2.修改和去掉配置方法,只支持vue自定义UI
1.4.0(2023-05-24)
本次主要更新: 1.andorid 增加 完全自定义拨号来电界面,来电秀
1.3.0(2023-05-17)
本次主要更新: 1.新增设置为默认电话应用 2.增加完全关闭app,也可以接听和挂断电话 3.增加相关监听
查看更多平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:6.0 - 14.0 | armeabi-v7a:支持,arm64-v8a:支持,x86:支持 | 适用版本区间:11 - 17 |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios
注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择
KJ-CallV2
电话状态监听、接听电话、挂断电话、回调手机号码、默认电话拨号应用、来去电黑白名单、支持不设置默认应用实现接听和挂断、完全关闭app也可以接听挂断电话(ios、android)
注意
1.ios版本:只有 电话状态监听 和 注销监听
2.andorid版本:不保证所有手机都有效,请试用合适再购买
3.如果设置为 默认电话应用,也可以实现完全关闭app,也可以接听和挂断电话
使用
<template>
<view class="content">
<button type="primary" @click="CallState">监听电话状态(双端)</button>
<view class="json">{{json}}</view>
<button type="primary" @click="unregisterReceiver">注销监听电话状态(双端)</button>
<button type="primary" @click="CallState_Andoird">监听电话状态(andorid)</button>
<view class="json">{{json_andorid}}</view>
<button type="primary" @click="unregisterReceiver_Andoird">注销监听电话状态(andorid)</button>
<button type="primary" @click="ringingCall">接听电话(andorid8.0以上不需要设置默认电话应用)</button>
<button type="primary" @click="endCall">挂断电话(andorid9.0以上不需要设置默认电话应用)</button>
<button type="primary" @click="isDefaultCall">判断本app是不是默认电话应用(andorid)</button>
<button type="primary" @click="setDefaultCall">设置默认电话应用,完全关闭app也可以接听挂断(andorid)</button>
<button type="primary" @click="setInCallServiceCallBack">监听电话状态2(andorid)</button>
<view class="json">{{json2}}</view>
<button type="primary" @click="cancelInCallServiceCallBack">取消监听电话状态2(andorid)</button>
<button type="primary" @click="ringingCallV2">接听电话(设置为默认电话应用才有效)</button>
<button type="primary" @click="endCallV2">挂断电话(设置为默认电话应用才有效)</button>
<button type="primary" @click="getInCallInfo">获取当前电话通信信息</button>
<view class="json">{{inCallInfo}}</view>
<button type="primary" @click="makePhoneCall">打电话/跳转到拨号界面UI</button>
</view>
</template>
<script>
var KJCallV2 = uni.requireNativePlugin("KJ-CallV2");
export default {
data() {
return {
json: null,
json_andorid: null,
json2: null,
inCallInfo: null
}
},
onLoad() {
console.log(plus.io.convertLocalFileSystemURL("hybrid/html/index.html"));
if (plus.os.name == 'Android') {
plus.android.requestPermissions(
['android.permission.ANSWER_PHONE_CALLS', //手动 挂断和接听 需要这个权限
"android.permission.MODIFY_AUDIO_SETTINGS", //手动 挂断和接听 需要这个权限
"android.permission.CALL_PHONE", //手动 挂断和接听 需要这个权限
"android.permission.READ_PHONE_STATE", //>监听电话状态 需要这个权限
"android.permission.READ_CALL_LOG", //获取号码需要这个权限
"android.permission.PROCESS_OUTGOING_CALLS"
],
function(resultObj) {
var result = 0;
for (var i = 0; i < resultObj.granted.length; i++) {
var grantedPermission = resultObj.granted[i];
console.log('已获取的权限:' + grantedPermission);
result = 1
}
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
var deniedPresentPermission = resultObj.deniedPresent[i];
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
result = 0
}
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
var deniedAlwaysPermission = resultObj.deniedAlways[i];
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
result = -1
}
},
function(error) {
console.log('申请权限错误:' + error.code + " = " + error.message);
}
);
}
//this.getInCallInfo();
this.setInCallServiceCallBack();
},
methods: {
CallState() {
//电话状态监听
KJCallV2.CallState((res) => {
this.json = JSON.stringify(res);
console.log("CallState: " + JSON.stringify(res));
if (plus.os.name == 'Android') {
console.log("电话号码:" + res.incomingNumber); //号码不一定能获取到
if (res.callState == "ringing") {
console.log("来电");
//this.endCall(); //来电自动挂断
//this.ringingCall(); //来电自动接听
} else if (res.callState == "offhook") { //拨打 和 接听都会回调这个
console.log("待机");
} else if (res.callState == "idle") { //挂断 和 没有任何操作 都会回调这个
console.log("没有状态");
}
} else {
console.log("电话唯一ID:" + res.callID);
if (res.callState == "Dialing") {
console.log("拨打");
} else if (res.callState == "Connected") {
console.log("电话接通");
} else if (res.callState == "Disconnected") {
console.log("电话挂断");
} else if (res.callState == "Incoming") {
console.log("来电");
} else if (res.callState == "Other") {
console.log("电话其他状态");
}
}
})
},
unregisterReceiver() {
KJCallV2.unregisterReceiver((res) => {
console.log("unregisterReceiver: " + JSON.stringify(res));
});
},
CallState_Andoird() {
//电话状态监听
KJCallV2.CallState_Andoird((res) => {
this.json_andorid = JSON.stringify(res);
console.log("CallState_Andoird: " + JSON.stringify(res));
if (plus.os.name == 'Android') {
console.log("手机号码:" + res.phoneNumber); //号码不一定能获取到
console.log("来电号码:" + res.incomingNumber); //号码不一定能获取到
console.log("去电号码:" + res.outNumber); //号码不一定能获取到
if (res.callState == "ringing") {
console.log("来电");
//this.endCall(); //来电自动挂断
//this.ringingCall(); //来电自动接听
} else if (res.callState == "offhook") { //拨打 和 接听都会回调这个
console.log("待机");
} else if (res.callState == "idle") { //挂断 和 没有任何操作 都会回调这个
console.log("没有状态");
}
}
})
},
unregisterReceiver_Andoird() {
KJCallV2.unregisterReceiver_Andoird((res) => {
console.log("unregisterReceiver_Andoird: " + JSON.stringify(res));
});
},
ringingCall() {
//注意事项:andorid 8.0以上 不需要设置为默认电话应用,也可以接听
KJCallV2.ringingCall((res) => {
console.log("ringingCall: " + JSON.stringify(res));
});
},
endCall() {
//注意事项:andorid 9.0以上 不需要设置为默认电话应用,也可以挂断
KJCallV2.endCall((res) => {
console.log("endCall: " + JSON.stringify(res));
});
},
isDefaultCall() {
KJCallV2.isDefaultCall((res) => {
console.log("isDefaultCall: " + JSON.stringify(res));
});
},
setDefaultCall() {
/**
* 自动接听和挂断是同步执行
* 来电,如果想先接后挂, ringing->ringingCall->delay设置为0 ringing->endCall->delay设置为2
* 如果设置为 默认电话应用,也可以实现完全关闭app,也可以接听和挂断电话
* 黑白名单执行顺序,先执行whiteList,再执行blackList
*
*
* 自定义来电去电界面方式:
* 1.设置isOpenApp为true,然后在 App.vue里onShow 和 setInCallServiceCallBack回调 写相关代码,具体看示例
* */
var dic = {
"isOpenApp": true, //来电或去电,是否打开app,可用以vue自定义来电去电界面
"dialUI": {
"url": plus.io.convertLocalFileSystemURL("hybrid/html/index2.html"),
//说明:如果是自定义拨号界面UI,最好设置一下,要打开app的时候,会有1s的白屏
},
"ringing": { //来电
"ringingCall": { //接听
"isAuto": true, //是否自动接听电话
"delay": 0, //延迟执行时间,单位秒
/**whiteList和blackList都设置为null,所有号码都执行**/
"whiteList": ["xxx"], //接听白名单,list里的号码,自动接听,反之不接听
//"blackList": ["xxx"], //接听黑名单,list里的号码,不接听,反之自动接听
},
"endCall": { //挂断
"isAuto": false, //是否自动挂断电话
"delay": 0, //延迟执行时间,单位秒
/**whiteList和blackList都设置为null,所有号码都执行**/
//"whiteList": ["xxx"], //挂断白名单,list里的号码,自动挂断,反之不挂断
"blackList": ["xxx"], //挂断黑名单,list里的号码,不挂断,反之自动挂断
}
},
"connecting": { //去电
"endCall": { //挂断
"isAuto": false, //是否自动挂断电话
"delay": 0, //延迟执行时间,单位秒
/**whiteList和blackList都设置为null,所有号码都执行**/
//"whiteList": ["xxx"], //挂断白名单,list里的号码,自动挂断,反之不挂断
"blackList": ["xxx"], //挂断黑名单,list里的号码,不挂断,反之自动挂断
}
}
}
KJCallV2.setDefaultCall(dic, (res) => {
console.log("setDefaultCall: " + JSON.stringify(res));
var code = res.code;
if (code == 0) {
var resultCode = res.resultCode;
if (resultCode == -1) {
console.log("已设为默认电话应用");
} else {
console.log("设为默认电话应用失败");
}
} else if (code == 1) {
console.log("跳到电话设置界面去设置");
} else if (code == 2) {
console.log("Android 6.0以上才支持修改默认电话应用!");
}
});
},
setInCallServiceCallBack() {
KJCallV2.setInCallServiceCallBack((res) => {
console.log("setInCallServiceCallBack: " + JSON.stringify(res));
/**
* 返回json字段说明:{"method":"onCallAdded","call":{"state":2,"details":{"phoneNumber":"xxx"}}}
* state - 状态 0(是一个新的连接,但是还没连接上) 1(一个处于外拨的连接,此时对方还没有应答,可以听到嘟嘟的声音) 2(来电)
* 3(一个连接存于hold状态) 4(一个连接处于活动状态,双方可以正常主动通信) 7(当前的call释放了所有资源处于断开连接状态)
* 9(外拨的初始状态) 10(正处于断开连接状态) 11(表示一个连接正处于从远端连接拉到本地连接的一个状态(比如有两个设备但是共用一个号码))
* */
this.json2 = JSON.stringify(res)
var method = res.method;
var state = res.call.state;
var phoneNumber = res.call.details.phoneNumber; //号码不一定能获取到
if (method == "onCallAdded") {
if (state == 2) {
console.log("来电:" + phoneNumber);
} else if (state == 9) {
console.log("去电:" + phoneNumber);
}
} else if (method == "onStateChanged") {
if (state == 4) {
console.log("通话中:" + phoneNumber);
} else if (state == 7) {
console.log("通话结束:" + phoneNumber);
}
}
});
},
cancelInCallServiceCallBack() {
KJCallV2.cancelInCallServiceCallBack();
},
ringingCallV2() {
//注意事项:需要设置为默认电话应用
KJCallV2.ringingCallV2();
},
endCallV2() {
//注意事项:需要设置为默认电话应用
KJCallV2.endCallV2();
},
getInCallInfo() {
KJCallV2.getInCallInfo((res) => {
this.inCallInfo = JSON.stringify(res);
if (res.result.state == 1 || res.result.state == 2) {
console.log("getInCallInfo:" + JSON.stringify(res));
uni.reLaunch({
url: "/pages/index/callUI"
})
}
});
},
isSystemAlertWindowPermission() {
KJCallV2.isSystemAlertWindowPermission((res) => {
console.log("isSystemAlertWindowPermission:" + JSON.stringify(res));
});
},
requestSystemAlertWindowPermission() {
//注意:需要在manifest.json配置android.permission.SYSTEM_ALERT_WINDOW权限
KJCallV2.requestSystemAlertWindowPermission((res) => {
console.log("requestSystemAlertWindowPermission:" + JSON.stringify(res));
});
},
saveCallUICacheData() {
//例如:实现更换壁纸的功能
var json = {
"wallpaperUrl": plus.io.convertLocalFileSystemURL("static/wallpaper2.jpeg")
}
var jsonStr = JSON.stringify(json);
KJCallV2.saveCallUICacheData(jsonStr);
},
saveCallUICacheData2() {
KJCallV2.saveCallUICacheData("");
},
makePhoneCall() {
uni.makePhoneCall({
phoneNumber: "10086"
})
}
}
}
</script>
<style>
.json {
word-wrap: break-word;
}
button {
font-size: 15px;
}
</style>
App.vue 跳转逻辑
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
var KJCallV2 = uni.requireNativePlugin("KJ-CallV2");
KJCallV2.getInCallInfo((res) => {
console.log(res);
if (res.result.isDial == false) { //vue 自定义来电和去电UI
uni.reLaunch({
url: "/pages/index/callUI"
})
} else if (res.result.isDial == true) { //自定义拨号界面UI
uni.navigateTo({
url: "/pages/index/dialUI?phoneNumber=" + res.result.phoneNumber
})
}
});
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>
vue 自定义来电去电页面
<template>
<view class="content">
<view class="top">
<view class="phoneNumber">{{phoneNumber}}</view>
<view class="time">{{time}}</view>
</view>
<view class="bottom">
<view class="call-btn" @click="endCallV2" v-if="state == 1 || state == 2 || state == 9 || state == 4">
<image src="../../static/endCall.png" />
</image>
<view>挂断</view>
</view>
<view class="call-btn" @click="ringingCallV2" v-if="state == 2">
<image src="../../static/ringingCall.png" />
</image>
<view>接听</view>
</view>
</view>
</view>
</template>
<script>
var KJCallV2 = uni.requireNativePlugin("KJ-CallV2");
export default {
data() {
return {
state: 1,
phoneNumber: "xxx",
time: "正在呼叫中...",
callingTime: 0
}
},
onLoad() {
this.getInCallInfo();
this.setInCallServiceCallBack();
KJCallV2.openSpeaker();
},
methods: {
setInCallServiceCallBack() {
KJCallV2.setInCallServiceCallBack((res) => {
console.log("setInCallServiceCallBack: " + JSON.stringify(res));
/**
* 返回json字段说明:{"method":"onCallAdded","call":{"state":2,"details":{"phoneNumber":"xxx"}}}
* state - 状态 0(是一个新的连接,但是还没连接上) 1(一个处于外拨的连接,此时对方还没有应答,可以听到嘟嘟的声音) 2(来电)
* 3(一个连接存于hold状态) 4(一个连接处于活动状态,双方可以正常主动通信) 7(当前的call释放了所有资源处于断开连接状态)
* 9(外拨的初始状态) 10(正处于断开连接状态) 11(表示一个连接正处于从远端连接拉到本地连接的一个状态(比如有两个设备但是共用一个号码))
* */
this.json2 = JSON.stringify(res)
var method = res.method;
var state = res.call.state;
var phoneNumber = res.call.details.phoneNumber; //号码不一定能获取到
if (method == "onCallAdded") {
this.callingTime = 0;
if (state == 2) {
console.log("来电:" + phoneNumber);
} else if (state == 9) {
console.log("去电:" + phoneNumber);
}
} else if (method == "onStateChanged") {
if (state == 4) {
console.log("通话中:" + phoneNumber);
setInterval((res) => {
this.callingTime++;
this.time = this.getTime(this.callingTime);
}, 1000)
} else if (state == 7) {
console.log("通话结束:" + phoneNumber);
this.callingTime = 0;
uni.reLaunch({
url: "/pages/index/index"
})
}
}
});
},
cancelInCallServiceCallBack() {
KJCallV2.cancelInCallServiceCallBack();
},
ringingCallV2() {
//注意事项:需要设置为默认电话应用
KJCallV2.ringingCallV2();
},
endCallV2() {
//注意事项:需要设置为默认电话应用
KJCallV2.endCallV2();
},
getInCallInfo() {
KJCallV2.getInCallInfo((res) => {
console.log("getInCallInfo:" + JSON.stringify(res));
this.state = res.result.state;
this.inCallInfo = JSON.stringify(res);
this.phoneNumber = res.result.phoneNumber
if (this.state == 2) {
this.time = "来电";
} else {
this.time = "正在呼叫中...";
}
});
},
getTime(time) {
let h = parseInt(time / 60 / 60 % 24)
h = h < 10 ? '0' + h : h
let m = parseInt(time / 60 % 60)
m = m < 10 ? '0' + m : m
let s = parseInt(time % 60)
s = s < 10 ? '0' + s : s
return h + ":" + m + ":" + s
}
}
}
</script>
<style>
.content {
text-align: center;
}
.top {
font-size: 40px;
height: 40vh;
background-color: #118801;
color: white;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
}
.phoneNumber {
margin-top: 16px;
}
.time {
font-size: 18px;
}
.bottom {
height: 60vh;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
justify-content: center;
}
.call-btn {
width: 50%;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 18px;
}
.call-btn image {
width: 80px;
height: 80px;
}
</style>
vue自定义拨号界面UI
<template>
<view class="content">
<view class="top">
<input class="uni-input" type="number" focus="true" v-model="phoneNumber" placeholder="Please enter"
@input="input" />
</view>
<view class="bottom">
<view class="call-btn" @click="dialCall">
<image src="../../static/ringingCall.png" />
</image>
<view>立即拨打</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
phoneNumber: "xxx",
}
},
onLoad(res) {
this.phoneNumber = res.phoneNumber;
},
methods: {
dialCall() {
//导入Activity、Intent类
var Intent = plus.android.importClass("android.content.Intent");
var Uri = plus.android.importClass("android.net.Uri");
// 获取主Activity对象的实例
var main = plus.android.runtimeMainActivity();
// 创建Intent
var uri = Uri.parse("tel:" + this.phoneNumber); // 这里可修改电话号码
var call = new Intent("android.intent.action.CALL", uri);
// 调用startActivity方法拨打电话
main.startActivity(call);
}
}
}
</script>
<style>
.content {
text-align: center;
}
.top {
font-size: 40px;
height: 20vh;
background-color: #118801;
color: white;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
}
.uni-input {
font-size: 40px;
}
.bottom {
height: 60vh;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
justify-content: center;
}
.call-btn {
width: 50%;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 18px;
}
.call-btn image {
width: 80px;
height: 80px;
}
</style>