更新记录

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

原生插件通用使用流程:

  1. 购买插件,选择该插件绑定的项目。
  2. 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
  3. 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
  4. 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
  5. 开发完毕后正式云打包

付费原生插件目前不支持离线打包。
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>

隐私、权限声明

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

andorid: 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.SYSTEM_ALERT_WINDOW

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

插件不采集任何数据

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

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问