HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

长期提供uniapp 插件/组件/页面定制开发

iOS Android nvue uniapp插件

长期提供uniapp 插件/组件/页面定制,vue/nvue均可。请看更多插件

欢迎各位老板进群叨扰 1091229222,加群请备注: uniapp社区

长期提供uniapp 插件/组件/页面定制,vue/nvue均可。请看更多插件

欢迎各位老板进群叨扰 1091229222,加群请备注: uniapp社区

uni-app uniCloud node.js支付宝网页支付开发心得

支付宝 uniCloud

支付宝网页支付的流程:前端请求支付宝支付表单参数->后端生成支付表单参数给前端->前端根据支付参数构建form表单对支付宝发起POST请求->支付宝支付成功POST异步通知开发者的服务器。

支付宝用到的是RSA加密,搞懂RSA加密的原理有助于理解支付宝支付流程。推荐李永乐老师讲非对称加密的视频。

这是已经写好的测试例子:https://static-b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/static/alipay-demo.html

代码很简单,直接看代码就理解了,注意点写在注释里。

请求支付参数的服务端代码:

'use strict'  
const NodeRSA = require('node-rsa') // 需要执行npm install node-rsa才能调用  
const querystring = require('querystring') // node自带,无需安装,直接调用  
exports.main = async (event, context) => {  
    const appId = '支付宝的appId'  
    let merchantPrivateKey = '支付宝商家公钥'  

    let ua = ''  
    try {  
        ua = event.headers['user-agent']  
    }catch(e){}  
    let productCode = 'FAST_INSTANT_TRADE_PAY'  
    let method = 'alipay.trade.page.pay'  
    if (ua.indexOf('Mobile') > -1) {  
        productCode = 'QUICK_WAP_WAY'  
        method = 'alipay.trade.wap.pay'  
    }  
    let bodyObj = querystring.parse(event.body) // url请求参数字符串转object。uni-app云函数实例化后,POST的请求参数在body里  
    if (Object.keys(bodyObj).length) {  
        const passbackParams = JSON.stringify({mobile: '18888888888', sku: 'year'}) // 开发者想要传递的参数,字符串,支付宝异步通知会带上这个  
        let bizContent = JSON.stringify({  
            subject: '支付宝测试-'+bodyObj.price+'元',  
            out_trade_no: (new Date()).getTime(),  
            total_amount: bodyObj.price,  
            product_code: productCode,  
            quit_url: 'https://imgbed.cn/static/alipay-return.html', // 手机网页支付放弃支付时返回的网址  
            passback_params: passbackParams  
        })  
        let queryObject = ksort({  
            app_id: appId,  
            biz_content: bizContent,  
            charset: 'UTF-8',  
            method: method,  
            notify_url: '支付宝异步通知地址',  
            return_url: 'https://imgbed.cn/static/alipay-return.html',  
            sign_type: 'RSA2',  
            timestamp: time2date((new Date()).getTime()), // Y-m-d H:i:s格式的字符串  
            version: '1.0'  
        })  
        const query = querystring.unescape(querystring.stringify(queryObject)) // 要再套一层querystring.unescape,否则query被转义,会导致签名的字符串跟支付宝不一致  
        const key = new NodeRSA(merchantPrivateKey, 'pkcs8-private')  
        const sign = key.sign(Buffer.from(query)).toString('base64')  
        queryObject.sign = sign  
        return {code:0, alipayParams: queryObject}  
    } else {  
        return {code: 1, msg: 'event.body is empty'}  
    }  

    /****************************************************************************************************/  
    // 毫秒时间戳转Y-m-d H:i:s或者Y-m-d  
    function add0(m){return m<10?'0'+m:m }  
    function time2date(shijianchuo, onlyDate) {  
        var time = new Date(shijianchuo);  
        var y = time.getFullYear();  
        var m = time.getMonth()+1;  
        var d = time.getDate();  
        var h = time.getHours();  
        var mm = time.getMinutes();  
        var s = time.getSeconds();  
        let returnStr = y+'-'+add0(m)+'-'+add0(d)  
        if (!onlyDate) {  
            returnStr += ' '+add0(h)+':'+add0(mm)+':'+add0(s)  
        }  
        return returnStr  
    }  

    // 对object的key进行排序  
    function ksort(params) {  
        let keys = Object.keys(params).sort();  
        let newParams = {};  
        keys.forEach((key) => {  
            newParams[key] = params[key];  
        });  
        return newParams;  
    }  
}

前端支付代码,为了方便,我用了vue+vant:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, viewport-fit=cover">  
        <title>支付宝支付演示</title>  
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.11/lib/index.css"/>  
    </head>  
    <body>  
        <div id='app' style="display: none; padding: 100px 15px 15px 15px;">  
            <div style="max-width: 512px; margin: 0 auto;">  
                <p><img height="44px" src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/dca221bb-2a37-4527-bea7-6fcb92945c18.png"></p>  
                <p v-for="price in prices"><van-button @click="alipay(price)" type="info" block>{{price}}元</van-button></p>  
            </div>  
            <div id="form-pay"></div>  

            <van-overlay :show="showLoading">  
                <div style="display: flex; align-items: center; justify-content: center; height: 100%">  
                    <van-loading size="24px" vertical>加载中...</van-loading>  
                </div>  
            </van-overlay>  
        </div>  
        <script src="https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>  
        <script src="https://cdn.jsdelivr.net/npm/vant@2.11/lib/vant.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js"></script>  
        <script>  
            const vm = new Vue({  
                el: '#app',  
                data() {  
                    return {  
                        prices: [0.01, 1, 10, 100],  
                        showLoading: false  
                    }  
                },  
                methods: {  
                    alipay(price) {  
                        if (navigator.userAgent.indexOf('MicroMessenger')>-1) {  
                            vant.Dialog({ message: '点击微信右上角···,选择“用浏览器打开”' })  
                        } else {  
                            this.showLoading = true  
                            axios({  
                                url: 'https://b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/http/alipay-params-demo', // uni-app云函数URL实例化的api  
                                data: Qs.stringify({price: price}),  
                                method: 'POST'  
                            }).then(res=>{  
                                this.showLoading = false  
                                if (0===res.data.code) {  
                                    const obj = res.data.alipayParams  
                                    const keys = Object.keys(obj)  
                                    let formHtml = ''  
                                    formHtml += '<meta charset="utf-8">'  
                                    formHtml += '<form id="alipaysubmit" method="POST" name="alipaysubmit" action="https://openapi.alipay.com/gateway.do?charset=UTF-8">'  
                                    for (i=0; i<keys.length; i++) {  
                                        formHtml += '<input type="hidden" name="'+keys[i]+'" value=\''+obj[keys[i]]+'\'>'  
                                    }  
                                    // formHtml += '<input type="submit">' // 手动提交表单  
                                    formHtml += '</form>'  
                                    $('#form-pay').html(formHtml)  
                                    document.forms["alipaysubmit"].submit() // 自动提交表单  
                                } else {  
                                    console.error(res.data.msg)  
                                }  
                            }).catch(err=>{  
                                this.showLoading = false  
                                console.error(err)  
                            })  
                        }  
                    }  
                }  
            })  
            $(document).ready(function(){  
                $('#app').show()  
            })  
        </script>  
    </body>  
</html>

支付成功后,支付宝会异步通知,开发者接收异步通知,验签,代码如下:

'use strict'  
const querystring = require('querystring')  
const NodeRSA = require('node-rsa')  
exports.main = async (event, context) => {  
    const alipayPublicKey = '支付宝公钥'  

    let bodyObj = ksort(querystring.parse(event.body))  
    if ('TRADE_SUCCESS'===bodyObj.trade_status) { // 支付失败也有可能会收到异步通知,所以这里要判断TRADE_SUCCESS  
        const outTradeNo = bodyObj.out_trade_no  
        if (outTradeNo是否已经存在于数据库) { // 异步通知可能会收到多次,判断out_trade_no是否存过数据库来判断重复通知  
            const sign = bodyObj.sign  
            delete bodyObj.sign  
            delete bodyObj.sign_type  
            const body = querystring.unescape(querystring.stringify(bodyObj, '&', '='))  
            const key = new NodeRSA(alipayPublicKey, 'pkcs8-public')  
            if (key.verify(Buffer.from(body), sign, 'utf8', 'base64')) { // 验签通过,继续执行业务代码  
                // 用户处理订单的代码  
            } else {  
                console.error('验签失败')  
            }  
        } else {  
            console.error('订单号已存在')  
        }  
    } else {  
        console.error('trade_status', bodyObj.trade_status)  
    }  

    return 'success' // 一定要返回'success'给支付宝,否则会重复多次通知  
}  

// 对object的key进行排序  
function ksort(params) {  
    let keys = Object.keys(params).sort();  
    let newParams = {};  
    keys.forEach((key) => {  
        newParams[key] = params[key];  
    });  
    return newParams;  
}

转载自:https://coding3.com/archives/unicloud-alipay.html

继续阅读 »

支付宝网页支付的流程:前端请求支付宝支付表单参数->后端生成支付表单参数给前端->前端根据支付参数构建form表单对支付宝发起POST请求->支付宝支付成功POST异步通知开发者的服务器。

支付宝用到的是RSA加密,搞懂RSA加密的原理有助于理解支付宝支付流程。推荐李永乐老师讲非对称加密的视频。

这是已经写好的测试例子:https://static-b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/static/alipay-demo.html

代码很简单,直接看代码就理解了,注意点写在注释里。

请求支付参数的服务端代码:

'use strict'  
const NodeRSA = require('node-rsa') // 需要执行npm install node-rsa才能调用  
const querystring = require('querystring') // node自带,无需安装,直接调用  
exports.main = async (event, context) => {  
    const appId = '支付宝的appId'  
    let merchantPrivateKey = '支付宝商家公钥'  

    let ua = ''  
    try {  
        ua = event.headers['user-agent']  
    }catch(e){}  
    let productCode = 'FAST_INSTANT_TRADE_PAY'  
    let method = 'alipay.trade.page.pay'  
    if (ua.indexOf('Mobile') > -1) {  
        productCode = 'QUICK_WAP_WAY'  
        method = 'alipay.trade.wap.pay'  
    }  
    let bodyObj = querystring.parse(event.body) // url请求参数字符串转object。uni-app云函数实例化后,POST的请求参数在body里  
    if (Object.keys(bodyObj).length) {  
        const passbackParams = JSON.stringify({mobile: '18888888888', sku: 'year'}) // 开发者想要传递的参数,字符串,支付宝异步通知会带上这个  
        let bizContent = JSON.stringify({  
            subject: '支付宝测试-'+bodyObj.price+'元',  
            out_trade_no: (new Date()).getTime(),  
            total_amount: bodyObj.price,  
            product_code: productCode,  
            quit_url: 'https://imgbed.cn/static/alipay-return.html', // 手机网页支付放弃支付时返回的网址  
            passback_params: passbackParams  
        })  
        let queryObject = ksort({  
            app_id: appId,  
            biz_content: bizContent,  
            charset: 'UTF-8',  
            method: method,  
            notify_url: '支付宝异步通知地址',  
            return_url: 'https://imgbed.cn/static/alipay-return.html',  
            sign_type: 'RSA2',  
            timestamp: time2date((new Date()).getTime()), // Y-m-d H:i:s格式的字符串  
            version: '1.0'  
        })  
        const query = querystring.unescape(querystring.stringify(queryObject)) // 要再套一层querystring.unescape,否则query被转义,会导致签名的字符串跟支付宝不一致  
        const key = new NodeRSA(merchantPrivateKey, 'pkcs8-private')  
        const sign = key.sign(Buffer.from(query)).toString('base64')  
        queryObject.sign = sign  
        return {code:0, alipayParams: queryObject}  
    } else {  
        return {code: 1, msg: 'event.body is empty'}  
    }  

    /****************************************************************************************************/  
    // 毫秒时间戳转Y-m-d H:i:s或者Y-m-d  
    function add0(m){return m<10?'0'+m:m }  
    function time2date(shijianchuo, onlyDate) {  
        var time = new Date(shijianchuo);  
        var y = time.getFullYear();  
        var m = time.getMonth()+1;  
        var d = time.getDate();  
        var h = time.getHours();  
        var mm = time.getMinutes();  
        var s = time.getSeconds();  
        let returnStr = y+'-'+add0(m)+'-'+add0(d)  
        if (!onlyDate) {  
            returnStr += ' '+add0(h)+':'+add0(mm)+':'+add0(s)  
        }  
        return returnStr  
    }  

    // 对object的key进行排序  
    function ksort(params) {  
        let keys = Object.keys(params).sort();  
        let newParams = {};  
        keys.forEach((key) => {  
            newParams[key] = params[key];  
        });  
        return newParams;  
    }  
}

前端支付代码,为了方便,我用了vue+vant:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, viewport-fit=cover">  
        <title>支付宝支付演示</title>  
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.11/lib/index.css"/>  
    </head>  
    <body>  
        <div id='app' style="display: none; padding: 100px 15px 15px 15px;">  
            <div style="max-width: 512px; margin: 0 auto;">  
                <p><img height="44px" src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/dca221bb-2a37-4527-bea7-6fcb92945c18.png"></p>  
                <p v-for="price in prices"><van-button @click="alipay(price)" type="info" block>{{price}}元</van-button></p>  
            </div>  
            <div id="form-pay"></div>  

            <van-overlay :show="showLoading">  
                <div style="display: flex; align-items: center; justify-content: center; height: 100%">  
                    <van-loading size="24px" vertical>加载中...</van-loading>  
                </div>  
            </van-overlay>  
        </div>  
        <script src="https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>  
        <script src="https://cdn.jsdelivr.net/npm/vant@2.11/lib/vant.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js"></script>  
        <script>  
            const vm = new Vue({  
                el: '#app',  
                data() {  
                    return {  
                        prices: [0.01, 1, 10, 100],  
                        showLoading: false  
                    }  
                },  
                methods: {  
                    alipay(price) {  
                        if (navigator.userAgent.indexOf('MicroMessenger')>-1) {  
                            vant.Dialog({ message: '点击微信右上角···,选择“用浏览器打开”' })  
                        } else {  
                            this.showLoading = true  
                            axios({  
                                url: 'https://b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/http/alipay-params-demo', // uni-app云函数URL实例化的api  
                                data: Qs.stringify({price: price}),  
                                method: 'POST'  
                            }).then(res=>{  
                                this.showLoading = false  
                                if (0===res.data.code) {  
                                    const obj = res.data.alipayParams  
                                    const keys = Object.keys(obj)  
                                    let formHtml = ''  
                                    formHtml += '<meta charset="utf-8">'  
                                    formHtml += '<form id="alipaysubmit" method="POST" name="alipaysubmit" action="https://openapi.alipay.com/gateway.do?charset=UTF-8">'  
                                    for (i=0; i<keys.length; i++) {  
                                        formHtml += '<input type="hidden" name="'+keys[i]+'" value=\''+obj[keys[i]]+'\'>'  
                                    }  
                                    // formHtml += '<input type="submit">' // 手动提交表单  
                                    formHtml += '</form>'  
                                    $('#form-pay').html(formHtml)  
                                    document.forms["alipaysubmit"].submit() // 自动提交表单  
                                } else {  
                                    console.error(res.data.msg)  
                                }  
                            }).catch(err=>{  
                                this.showLoading = false  
                                console.error(err)  
                            })  
                        }  
                    }  
                }  
            })  
            $(document).ready(function(){  
                $('#app').show()  
            })  
        </script>  
    </body>  
</html>

支付成功后,支付宝会异步通知,开发者接收异步通知,验签,代码如下:

'use strict'  
const querystring = require('querystring')  
const NodeRSA = require('node-rsa')  
exports.main = async (event, context) => {  
    const alipayPublicKey = '支付宝公钥'  

    let bodyObj = ksort(querystring.parse(event.body))  
    if ('TRADE_SUCCESS'===bodyObj.trade_status) { // 支付失败也有可能会收到异步通知,所以这里要判断TRADE_SUCCESS  
        const outTradeNo = bodyObj.out_trade_no  
        if (outTradeNo是否已经存在于数据库) { // 异步通知可能会收到多次,判断out_trade_no是否存过数据库来判断重复通知  
            const sign = bodyObj.sign  
            delete bodyObj.sign  
            delete bodyObj.sign_type  
            const body = querystring.unescape(querystring.stringify(bodyObj, '&', '='))  
            const key = new NodeRSA(alipayPublicKey, 'pkcs8-public')  
            if (key.verify(Buffer.from(body), sign, 'utf8', 'base64')) { // 验签通过,继续执行业务代码  
                // 用户处理订单的代码  
            } else {  
                console.error('验签失败')  
            }  
        } else {  
            console.error('订单号已存在')  
        }  
    } else {  
        console.error('trade_status', bodyObj.trade_status)  
    }  

    return 'success' // 一定要返回'success'给支付宝,否则会重复多次通知  
}  

// 对object的key进行排序  
function ksort(params) {  
    let keys = Object.keys(params).sort();  
    let newParams = {};  
    keys.forEach((key) => {  
        newParams[key] = params[key];  
    });  
    return newParams;  
}

转载自:https://coding3.com/archives/unicloud-alipay.html

收起阅读 »

初见鸿蒙,我觉得吧,嘿嘿。

鸿蒙采用了JS和JAVA混合开发模式。
惊喜的发现鸿蒙的编写方式和UNI都很像。
我觉得吧,未来还会有更多的惊喜的。
包括这里。哈哈。

鸿蒙采用了JS和JAVA混合开发模式。
惊喜的发现鸿蒙的编写方式和UNI都很像。
我觉得吧,未来还会有更多的惊喜的。
包括这里。哈哈。

微信小程序预览图片,因版权问题,不让用户下载,保存

解决方法:uni.previewImage({ current: src, // 当前显示图片的http链接 urls: [src], showmenu: false, })
起初看微信小程序,官网给的api 定义 wx.previewImage(Object object, boolean showmenu),这个是问题的, boolean showmenu 这个也是要写在对象里面的

uniapp 中也可以使用这个方法,但是文档还没有更新,希望尽快更新

继续阅读 »

解决方法:uni.previewImage({ current: src, // 当前显示图片的http链接 urls: [src], showmenu: false, })
起初看微信小程序,官网给的api 定义 wx.previewImage(Object object, boolean showmenu),这个是问题的, boolean showmenu 这个也是要写在对象里面的

uniapp 中也可以使用这个方法,但是文档还没有更新,希望尽快更新

收起阅读 »

什么是苹果TF签名?

iOS 上架 签名

近期由于ios企业签名的动荡出现的频繁掉签,超级签名的价格也一直居高不下。

TF签名再度出现到大家的视野,它独有的稳定性和超级大容量的安装设备,价格似乎也比较合适,因此广受APP运营商的追捧。

TF签名是什么?

TF签名其实只是行业内的叫法,它的全称是TestFlight,是苹果官方认可的一种应用测试渠道,所有要上架到TestFlight应用商店的APP都需要经过苹果官方的审核。

成功上架到TestFlight应用商店后,用户可以通过公开的链接进入TestFlight应用商店并加入到该APP版本的内测中。

TF签名有什么优势?

1、TF签名更稳定

前面提到过,凡是要上架到TestFlight应用商店的APP都是需要通过苹果官方的审核的,一旦成功上架到TestFlight应用商店,一般不会轻易掉签,除非你在后期的运营中出现

违规内容被用户举报或者恶意刷量。

值得一提的是,TestFlight的使用期限是3个月,它可以安装1万台手机,即使过期或者超过1万台安装设备了只要你的应用服务还在,对已安装该应用的设备是不会受到影响

的,这时你只需要重新上架生成新的TestFlight下载链接提供到新用户下载安装即可。

2、TF签名价格适中

如果你是一名IOS开发者,可以独立操作上架到TestFlight是不需要一毛钱的。这里主要针对普通的APP运营商,他们对TestFlight上架的流程和政策并不熟悉,所以需要寻求专

业平台的帮助。

按了解,TestFlight代上架的计费方式有按月、按季度,按月的话一般只需要2000元上下,按季度在5000元左右。其价格和独立版企业签名差不多,不同的是,TF签名即使掉

签也不会影响到已安装的应用的用户。

是否存在永久不掉签的TF签名?

在网上看到有不少打着永不掉签的噱头的商家很多,往往也有不少APP运营商被这所谓的“永不掉签”所迷惑。

在这里,我可以负责任的告诉你,遇到打着这种噱头的商家,一定要提高警惕。

即使是超级签名,也没有绝对的稳定可言,因为你知道这些平台会给你用的那种号做签名,如果是一些境外号或者调查号说不定几天就掉签了。

TF签名掉签的可能性一般出现在运营商,如果在运营的过程中出现违规的内容被用户或同行举报触发了苹果的审核,掉签是肯定的。

最后值得注意的是,由于TestFlight上架需要通过苹果官方的审核,一些涉及违规内容的应用基本是上架无望。如果通过特殊的技术规避比如采用加壳/换壳等手段,能够成功

上架到TestFlgith的可能还是有的,这也是马甲包的一种。

如果有特殊应用需要TF签名的,可以联系我,专业做APP代上架:

Q:1481983952


继续阅读 »

近期由于ios企业签名的动荡出现的频繁掉签,超级签名的价格也一直居高不下。

TF签名再度出现到大家的视野,它独有的稳定性和超级大容量的安装设备,价格似乎也比较合适,因此广受APP运营商的追捧。

TF签名是什么?

TF签名其实只是行业内的叫法,它的全称是TestFlight,是苹果官方认可的一种应用测试渠道,所有要上架到TestFlight应用商店的APP都需要经过苹果官方的审核。

成功上架到TestFlight应用商店后,用户可以通过公开的链接进入TestFlight应用商店并加入到该APP版本的内测中。

TF签名有什么优势?

1、TF签名更稳定

前面提到过,凡是要上架到TestFlight应用商店的APP都是需要通过苹果官方的审核的,一旦成功上架到TestFlight应用商店,一般不会轻易掉签,除非你在后期的运营中出现

违规内容被用户举报或者恶意刷量。

值得一提的是,TestFlight的使用期限是3个月,它可以安装1万台手机,即使过期或者超过1万台安装设备了只要你的应用服务还在,对已安装该应用的设备是不会受到影响

的,这时你只需要重新上架生成新的TestFlight下载链接提供到新用户下载安装即可。

2、TF签名价格适中

如果你是一名IOS开发者,可以独立操作上架到TestFlight是不需要一毛钱的。这里主要针对普通的APP运营商,他们对TestFlight上架的流程和政策并不熟悉,所以需要寻求专

业平台的帮助。

按了解,TestFlight代上架的计费方式有按月、按季度,按月的话一般只需要2000元上下,按季度在5000元左右。其价格和独立版企业签名差不多,不同的是,TF签名即使掉

签也不会影响到已安装的应用的用户。

是否存在永久不掉签的TF签名?

在网上看到有不少打着永不掉签的噱头的商家很多,往往也有不少APP运营商被这所谓的“永不掉签”所迷惑。

在这里,我可以负责任的告诉你,遇到打着这种噱头的商家,一定要提高警惕。

即使是超级签名,也没有绝对的稳定可言,因为你知道这些平台会给你用的那种号做签名,如果是一些境外号或者调查号说不定几天就掉签了。

TF签名掉签的可能性一般出现在运营商,如果在运营的过程中出现违规的内容被用户或同行举报触发了苹果的审核,掉签是肯定的。

最后值得注意的是,由于TestFlight上架需要通过苹果官方的审核,一些涉及违规内容的应用基本是上架无望。如果通过特殊的技术规避比如采用加壳/换壳等手段,能够成功

上架到TestFlgith的可能还是有的,这也是马甲包的一种。

如果有特殊应用需要TF签名的,可以联系我,专业做APP代上架:

Q:1481983952


收起阅读 »

个人承接H5、小程序、网站等,技术主要是uni app、vue、react、taro等,如果有需求,价格美丽,欢迎来聊

外包

1.经验丰富,做过多种类型项目;
2.代码规范,
3.技术技术栈丰富;
4.喜欢纯前端的活,如果有需要后端的,我可以联系
5.已有跟其他老板合作非常顺利,但是也有遇到谈好的价格,最后鸡蛋里面挑骨头,希望来找我的老板,我们都能合作愉快;
6.希望我们互利共赢
7.wx: 18896963944, 备注 uni app

继续阅读 »

1.经验丰富,做过多种类型项目;
2.代码规范,
3.技术技术栈丰富;
4.喜欢纯前端的活,如果有需要后端的,我可以联系
5.已有跟其他老板合作非常顺利,但是也有遇到谈好的价格,最后鸡蛋里面挑骨头,希望来找我的老板,我们都能合作愉快;
6.希望我们互利共赢
7.wx: 18896963944, 备注 uni app

收起阅读 »

uniapp Android调用各种权限(主要解决webview使用相机无法获取权限)

相机权限


代码如下:

    onLaunch: function() {  
        console.log('onLaunch')  
        //监听底部中间菜单的事件  
        uni.onTabBarMidButtonTap(()=>{  
            plus.android.requestPermissions(['android.permission.CAMERA'], function(e){  
                if(e.deniedAlways.length>0){ //权限被永久拒绝  
                    // 弹出提示框解释为何需要权限,引导用户打开设置页面开启  
                    console.log('权限被永久拒绝'+e.deniedAlways.toString());  
                }  
                if(e.deniedPresent.length>0){    //权限被临时拒绝  
                    // 弹出提示框解释为何需要权限,可再次调用plus.android.requestPermissions申请权限  
                    console.log('权限被临时拒绝'+e.deniedPresent.toString());  
                }  
                if(e.granted.length>0){  //权限被允许  
                    console.log('权限被允许'+e.granted.toString());  
                }  
            }, function(e){  
                     console.log('Request Permissions error:'+JSON.stringify(e));  
            });  
            uni.navigateTo({  
                url:'/pages/mirror/index'  
            })  
        });  
    },  

更多Android权限列表

权限 名称 描述
android.permission.ACCESS_CHECKIN_PROPERTIES 访问登记属性 读取或写入登记check-in数据库属性表的权限
android.permission.ACCESS_COARSE_LOCATION 获取错略位置 通过WiFi或移动基站的方式获取用户错略的经纬度信息,定位精度大概误差在30~1500米
android.permission.ACCESS_FINE_LOCATION 获取精确位置 通过GPS芯片接收卫星的定位信息,定位精度达10米以内
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS 访问定位额外命令 允许程序访问额外的定位提供者指令
android.permission.ACCESS_MOCK_LOCATION 获取模拟定位信息 获取模拟定位信息,一般用于帮助开发者调试应用
android.permission.ACCESS_NETWORK_STATE 获取网络状态 获取网络信息状态,如当前的网络连接是否有效
android.permission.ACCESS_SURFACE_FLINGER 访问Surface Flinger Android平台上底层的图形显示支持,一般用于游戏或照相机预览界面和底层模式的屏幕截图
android.permission.ACCESS_WIFI_STATE 获取WiFi状态 获取当前WiFi接入的状态以及WLAN热点的信息
android.permission.ACCOUNT_MANAGER 账户管理 获取账户验证信息,主要为GMail账户信息,只有系统级进程才能访问的权限
android.permission.AUTHENTICATE_ACCOUNTS 验证账户 允许一个程序通过账户验证方式访问账户管理ACCOUNT_MANAGER相关信息
android.permission.BATTERY_STATS 电量统计 获取电池电量统计信息
android.permission.BIND_APPWIDGET 绑定小插件 允许一个程序告诉appWidget服务需要访问小插件的数据库,只有非常少的应用才用到此权限
android.permission.BIND_DEVICE_ADMIN 绑定设备管理 请求系统管理员接收者receiver,只有系统才能使用
android.permission.BIND_INPUT_METHOD 绑定输入法 请求InputMethodService服务,只有系统才能使用
android.permission.BIND_REMOTEVIEWS 绑定RemoteView 必须通过RemoteViewsService服务来请求,只有系统才能用
android.permission.BIND_WALLPAPER 绑定壁纸 必须通过WallpaperService服务来请求,只有系统才能用
android.permission.BLUETOOTH 使用蓝牙 允许程序连接配对过的蓝牙设备
android.permission.BLUETOOTH_ADMIN 蓝牙管理 允许程序进行发现和配对新的蓝牙设备
android.permission.BRICK 变成砖头 能够禁用手机,非常危险,顾名思义就是让手机变成砖头
android.permission.BROADCAST_PACKAGE_REMOVED 应用删除时广播 当一个应用在删除时触发一个广播
android.permission.BROADCAST_SMS 收到短信时广播 当收到短信时触发一个广播
android.permission.BROADCAST_STICKY 连续广播 允许一个程序收到广播后快速收到下一个广播
android.permission.BROADCAST_WAP_PUSH WAP PUSH广播 WAP PUSH服务收到后触发一个广播
android.permission.CALL_PHONE 拨打电话 允许程序从非系统拨号器里输入电话号码
android.permission.CALL_PRIVILEGED 通话权限 允许程序拨打电话,替换系统的拨号器界面
android.permission.CAMERA 拍照权限 允许访问摄像头进行拍照
android.permission.CHANGE_COMPONENT_ENABLED_STATE 改变组件状态 改变组件是否启用状态
android.permission.CHANGE_CONFIGURATION 改变配置 允许当前应用改变配置,如定位
android.permission.CHANGE_NETWORK_STATE 改变网络状态 改变网络状态如是否能联网
android.permission.CHANGE_WIFI_MULTICAST_STATE 改变WiFi多播状态 改变WiFi多播状态
android.permission.CHANGE_WIFI_STATE 改变WiFi状态 改变WiFi状态
android.permission.CLEAR_APP_CACHE 清除应用缓存 清除应用缓存
android.permission.CLEAR_APP_USER_DATA 清除用户数据 清除应用的用户数据
android.permission.CWJ_GROUP 底层访问权限 允许CWJ账户组访问底层信息
android.permission.CELL_PHONE_MASTER_EX 手机优化大师扩展权限 手机优化大师扩展权限
android.permission.CONTROL_LOCATION_UPDATES 控制定位更新 允许获得移动网络定位信息改变
android.permission.DELETE_CACHE_FILES 删除缓存文件 允许应用删除缓存文件
android.permission.DELETE_PACKAGES 删除应用 允许程序删除应用
android.permission.DEVICE_POWER 电源管理 允许访问底层电源管理
android.permission.DIAGNOSTIC 应用诊断 允许程序到RW到诊断资源
android.permission.DISABLE_KEYGUARD 禁用键盘锁 允许程序禁用键盘锁
android.permission.DUMP 转存系统信息 允许程序获取系统dump信息从系统服务
android.permission.EXPAND_STATUS_BAR 状态栏控制 允许程序扩展或收缩状态栏
android.permission.FACTORY_TEST 工厂测试模式 允许程序运行工厂测试模式
android.permission.FLASHLIGHT 使用闪光灯 允许访问闪光灯
android.permission.FORCE_BACK 强制后退 允许程序强制使用back后退按键,无论Activity是否在顶层
android.permission.GET_ACCOUNTS 访问账户Gmail列表 访问GMail账户列表
android.permission.GET_PACKAGE_SIZE 获取应用大小 获取应用的文件大小
android.permission.GET_TASKS 获取任务信息 允许程序获取当前或最近运行的应用
android.permission.GLOBAL_SEARCH 允许全局搜索 允许程序使用全局搜索功能
android.permission.HARDWARE_TEST 硬件测试 访问硬件辅助设备,用于硬件测试
android.permission.INJECT_EVENTS 注射事件 允许访问本程序的底层事件,获取按键、轨迹球的事件流
android.permission.INSTALL_LOCATION_PROVIDER 安装定位提供 安装定位提供
android.permission.INSTALL_PACKAGES 安装应用程序 允许程序安装应用
android.permission.INTERNAL_SYSTEM_WINDOW 内部系统窗口 允许程序打开内部窗口,不对第三方应用程序开放此权限
android.permission.INTERNET 访问网络 访问网络连接,可能产生GPRS流量
android.permission.KILL_BACKGROUND_PROCESSES 结束后台进程 允许程序调用killBackgroundProcesses(String).方法结束后台进程
android.permission.MANAGE_ACCOUNTS 管理账户 允许程序管理AccountManager中的账户列表
android.permission.MANAGE_APP_TOKENS 管理程序引用 管理创建、摧毁、Z轴顺序,仅用于系统
android.permission.MTWEAK_USER 高级权限 允许mTweak用户访问高级系统权限
android.permission.MTWEAK_FORUM 社区权限 允许使用mTweak社区权限
android.permission.MASTER_CLEAR 软格式化 允许程序执行软格式化,删除系统配置信息
android.permission.MODIFY_AUDIO_SETTINGS 修改声音设置 修改声音设置信息
android.permission.MODIFY_PHONE_STATE 修改电话状态 修改电话状态,如飞行模式,但不包含替换系统拨号器界面
android.permission.MOUNT_FORMAT_FILESYSTEMS 格式化文件系统 格式化可移动文件系统,比如格式化清空SD卡
android.permission.MOUNT_UNMOUNT_FILESYSTEMS 挂载文件系统 挂载、反挂载外部文件系统
android.permission.NFC 允许NFC通讯 允许程序执行NFC近距离通讯操作,用于移动支持
android.permission.PERSISTENT_ACTIVITY 永久Activity 创建一个永久的Activity,该功能标记为将来将被移除
android.permission.PROCESS_OUTGOING_CALLS 处理拨出电话 允许程序监视,修改或放弃播出电话
android.permission.READ_CALENDAR 读取日程提醒 允许程序读取用户的日程信息
android.permission.READ_CONTACTS 读取联系人 允许应用访问联系人通讯录信息
android.permission.READ_FRAME_BUFFER 屏幕截图 读取帧缓存用于屏幕截图
com.android.browser.permission.READ_HISTORY_BOOKMARKS 读取收藏夹和历史记录 读取浏览器收藏夹和历史记录
android.permission.READ_INPUT_STATE 读取输入状态 读取当前键的输入状态,仅用于系统
android.permission.READ_LOGS 读取系统日志 读取系统底层日志
android.permission.READ_PHONE_STATE 读取电话状态 访问电话状态
android.permission.READ_SMS 读取短信内容 读取短信内容
android.permission.READ_SYNC_SETTINGS 读取同步设置 读取同步设置,读取Google在线同步设置
android.permission.READ_SYNC_STATS 读取同步状态 读取同步状态,获得Google在线同步状态
android.permission.REBOOT 重启设备 允许程序重新启动设备
android.permission.RECEIVE_BOOT_COMPLETED 开机自动允许 允许程序开机自动运行
android.permission.RECEIVE_MMS 接收彩信 接收彩信
android.permission.RECEIVE_SMS 接收短信 接收短信
android.permission.RECEIVE_WAP_PUSH 接收Wap Push 接收WAP PUSH信息
android.permission.RECORD_AUDIO 录音 录制声音通过手机或耳机的麦克
android.permission.REORDER_TASKS 排序系统任务 重新排序系统Z轴运行中的任务
android.permission.RESTART_PACKAGES 结束系统任务 结束任务通过restartPackage(String)方法,该方式将在外来放弃
android.permission.SEND_SMS 发送短信 发送短信
android.permission.SET_ACTIVITY_WATCHER 设置Activity观察其 设置Activity观察器一般用于monkey测试
com.android.alarm.permission.SET_ALARM 设置闹铃提醒 设置闹铃提醒
android.permission.SET_ALWAYS_FINISH 设置总是退出 设置程序在后台是否总是退出
android.permission.SET_ANIMATION_SCALE 设置动画缩放 设置全局动画缩放
android.permission.SET_DEBUG_APP 设置调试程序 设置调试程序,一般用于开发
android.permission.SET_ORIENTATION 设置屏幕方向 设置屏幕方向为横屏或标准方式显示,不用于普通应用
android.permission.SET_PREFERRED_APPLICATIONS 设置应用参数 设置应用的参数,已不再工作具体查看addPackageToPreferred(String)介绍
android.permission.SET_PROCESS_LIMIT 设置进程限制 允许程序设置最大的进程数量的限制
android.permission.SET_TIME 设置系统时间 设置系统时间
android.permission.SET_TIME_ZONE 设置系统时区 设置系统时区
android.permission.SET_WALLPAPER 设置桌面壁纸 设置桌面壁纸
android.permission.SET_WALLPAPER_HINTS 设置壁纸建议 设置壁纸建议
android.permission.SIGNAL_PERSISTENT_PROCESSES 发送永久进程信号 发送一个永久的进程信号
android.permission.STATUS_BAR 状态栏控制 允许程序打开、关闭、禁用状态栏
android.permission.SUBSCRIBED_FEEDS_READ 访问订阅内容 访问订阅信息的数据库
android.permission.SUBSCRIBED_FEEDS_WRITE 写入订阅内容 写入或修改订阅内容的数据库
android.permission.SYSTEM_ALERT_WINDOW 显示系统窗口 显示系统窗口
android.permission.UPDATE_DEVICE_STATS 更新设备状态 更新设备状态
android.permission.USE_CREDENTIALS 使用证书 允许程序请求验证从AccountManager
android.permission.USE_SIP 使用SIP视频 允许程序使用SIP视频服务
android.permission.VIBRATE 使用振动 允许振动
android.permission.WAKE_LOCK 唤醒锁定 允许程序在手机屏幕关闭后后台进程仍然运行
android.permission.WRITE_APN_SETTINGS 写入GPRS接入点设置 写入网络GPRS接入点设置
android.permission.WRITE_CALENDAR 写入日程提醒 写入日程,但不可读取
android.permission.WRITE_CONTACTS 写系人 写系人,但不可读取
android.permission.WRITE_EXTERNAL_STORAGE 写入外部存储 允许程序写入外部存储,如SD卡上写文件
android.permission.WRITE_GSERVICES 写入Google地图数据 允许程序写入Google Map服务数据
com.android.browser.permission.WRITE_HISTORY_BOOKMARKS 写入收藏夹和历史记录 写入浏览器历史记录或收藏夹,但不可读取
android.permission.WRITE_SECURE_SETTINGS 读写系统敏感设置 允许程序读写系统安全敏感的设置项
android.permission.WRITE_SETTINGS 读写系统设置 允许读写系统设置项
android.permission.WRITE_SMS 编写短信 允许编写短信

继续阅读 »


代码如下:

    onLaunch: function() {  
        console.log('onLaunch')  
        //监听底部中间菜单的事件  
        uni.onTabBarMidButtonTap(()=>{  
            plus.android.requestPermissions(['android.permission.CAMERA'], function(e){  
                if(e.deniedAlways.length>0){ //权限被永久拒绝  
                    // 弹出提示框解释为何需要权限,引导用户打开设置页面开启  
                    console.log('权限被永久拒绝'+e.deniedAlways.toString());  
                }  
                if(e.deniedPresent.length>0){    //权限被临时拒绝  
                    // 弹出提示框解释为何需要权限,可再次调用plus.android.requestPermissions申请权限  
                    console.log('权限被临时拒绝'+e.deniedPresent.toString());  
                }  
                if(e.granted.length>0){  //权限被允许  
                    console.log('权限被允许'+e.granted.toString());  
                }  
            }, function(e){  
                     console.log('Request Permissions error:'+JSON.stringify(e));  
            });  
            uni.navigateTo({  
                url:'/pages/mirror/index'  
            })  
        });  
    },  

更多Android权限列表

权限 名称 描述
android.permission.ACCESS_CHECKIN_PROPERTIES 访问登记属性 读取或写入登记check-in数据库属性表的权限
android.permission.ACCESS_COARSE_LOCATION 获取错略位置 通过WiFi或移动基站的方式获取用户错略的经纬度信息,定位精度大概误差在30~1500米
android.permission.ACCESS_FINE_LOCATION 获取精确位置 通过GPS芯片接收卫星的定位信息,定位精度达10米以内
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS 访问定位额外命令 允许程序访问额外的定位提供者指令
android.permission.ACCESS_MOCK_LOCATION 获取模拟定位信息 获取模拟定位信息,一般用于帮助开发者调试应用
android.permission.ACCESS_NETWORK_STATE 获取网络状态 获取网络信息状态,如当前的网络连接是否有效
android.permission.ACCESS_SURFACE_FLINGER 访问Surface Flinger Android平台上底层的图形显示支持,一般用于游戏或照相机预览界面和底层模式的屏幕截图
android.permission.ACCESS_WIFI_STATE 获取WiFi状态 获取当前WiFi接入的状态以及WLAN热点的信息
android.permission.ACCOUNT_MANAGER 账户管理 获取账户验证信息,主要为GMail账户信息,只有系统级进程才能访问的权限
android.permission.AUTHENTICATE_ACCOUNTS 验证账户 允许一个程序通过账户验证方式访问账户管理ACCOUNT_MANAGER相关信息
android.permission.BATTERY_STATS 电量统计 获取电池电量统计信息
android.permission.BIND_APPWIDGET 绑定小插件 允许一个程序告诉appWidget服务需要访问小插件的数据库,只有非常少的应用才用到此权限
android.permission.BIND_DEVICE_ADMIN 绑定设备管理 请求系统管理员接收者receiver,只有系统才能使用
android.permission.BIND_INPUT_METHOD 绑定输入法 请求InputMethodService服务,只有系统才能使用
android.permission.BIND_REMOTEVIEWS 绑定RemoteView 必须通过RemoteViewsService服务来请求,只有系统才能用
android.permission.BIND_WALLPAPER 绑定壁纸 必须通过WallpaperService服务来请求,只有系统才能用
android.permission.BLUETOOTH 使用蓝牙 允许程序连接配对过的蓝牙设备
android.permission.BLUETOOTH_ADMIN 蓝牙管理 允许程序进行发现和配对新的蓝牙设备
android.permission.BRICK 变成砖头 能够禁用手机,非常危险,顾名思义就是让手机变成砖头
android.permission.BROADCAST_PACKAGE_REMOVED 应用删除时广播 当一个应用在删除时触发一个广播
android.permission.BROADCAST_SMS 收到短信时广播 当收到短信时触发一个广播
android.permission.BROADCAST_STICKY 连续广播 允许一个程序收到广播后快速收到下一个广播
android.permission.BROADCAST_WAP_PUSH WAP PUSH广播 WAP PUSH服务收到后触发一个广播
android.permission.CALL_PHONE 拨打电话 允许程序从非系统拨号器里输入电话号码
android.permission.CALL_PRIVILEGED 通话权限 允许程序拨打电话,替换系统的拨号器界面
android.permission.CAMERA 拍照权限 允许访问摄像头进行拍照
android.permission.CHANGE_COMPONENT_ENABLED_STATE 改变组件状态 改变组件是否启用状态
android.permission.CHANGE_CONFIGURATION 改变配置 允许当前应用改变配置,如定位
android.permission.CHANGE_NETWORK_STATE 改变网络状态 改变网络状态如是否能联网
android.permission.CHANGE_WIFI_MULTICAST_STATE 改变WiFi多播状态 改变WiFi多播状态
android.permission.CHANGE_WIFI_STATE 改变WiFi状态 改变WiFi状态
android.permission.CLEAR_APP_CACHE 清除应用缓存 清除应用缓存
android.permission.CLEAR_APP_USER_DATA 清除用户数据 清除应用的用户数据
android.permission.CWJ_GROUP 底层访问权限 允许CWJ账户组访问底层信息
android.permission.CELL_PHONE_MASTER_EX 手机优化大师扩展权限 手机优化大师扩展权限
android.permission.CONTROL_LOCATION_UPDATES 控制定位更新 允许获得移动网络定位信息改变
android.permission.DELETE_CACHE_FILES 删除缓存文件 允许应用删除缓存文件
android.permission.DELETE_PACKAGES 删除应用 允许程序删除应用
android.permission.DEVICE_POWER 电源管理 允许访问底层电源管理
android.permission.DIAGNOSTIC 应用诊断 允许程序到RW到诊断资源
android.permission.DISABLE_KEYGUARD 禁用键盘锁 允许程序禁用键盘锁
android.permission.DUMP 转存系统信息 允许程序获取系统dump信息从系统服务
android.permission.EXPAND_STATUS_BAR 状态栏控制 允许程序扩展或收缩状态栏
android.permission.FACTORY_TEST 工厂测试模式 允许程序运行工厂测试模式
android.permission.FLASHLIGHT 使用闪光灯 允许访问闪光灯
android.permission.FORCE_BACK 强制后退 允许程序强制使用back后退按键,无论Activity是否在顶层
android.permission.GET_ACCOUNTS 访问账户Gmail列表 访问GMail账户列表
android.permission.GET_PACKAGE_SIZE 获取应用大小 获取应用的文件大小
android.permission.GET_TASKS 获取任务信息 允许程序获取当前或最近运行的应用
android.permission.GLOBAL_SEARCH 允许全局搜索 允许程序使用全局搜索功能
android.permission.HARDWARE_TEST 硬件测试 访问硬件辅助设备,用于硬件测试
android.permission.INJECT_EVENTS 注射事件 允许访问本程序的底层事件,获取按键、轨迹球的事件流
android.permission.INSTALL_LOCATION_PROVIDER 安装定位提供 安装定位提供
android.permission.INSTALL_PACKAGES 安装应用程序 允许程序安装应用
android.permission.INTERNAL_SYSTEM_WINDOW 内部系统窗口 允许程序打开内部窗口,不对第三方应用程序开放此权限
android.permission.INTERNET 访问网络 访问网络连接,可能产生GPRS流量
android.permission.KILL_BACKGROUND_PROCESSES 结束后台进程 允许程序调用killBackgroundProcesses(String).方法结束后台进程
android.permission.MANAGE_ACCOUNTS 管理账户 允许程序管理AccountManager中的账户列表
android.permission.MANAGE_APP_TOKENS 管理程序引用 管理创建、摧毁、Z轴顺序,仅用于系统
android.permission.MTWEAK_USER 高级权限 允许mTweak用户访问高级系统权限
android.permission.MTWEAK_FORUM 社区权限 允许使用mTweak社区权限
android.permission.MASTER_CLEAR 软格式化 允许程序执行软格式化,删除系统配置信息
android.permission.MODIFY_AUDIO_SETTINGS 修改声音设置 修改声音设置信息
android.permission.MODIFY_PHONE_STATE 修改电话状态 修改电话状态,如飞行模式,但不包含替换系统拨号器界面
android.permission.MOUNT_FORMAT_FILESYSTEMS 格式化文件系统 格式化可移动文件系统,比如格式化清空SD卡
android.permission.MOUNT_UNMOUNT_FILESYSTEMS 挂载文件系统 挂载、反挂载外部文件系统
android.permission.NFC 允许NFC通讯 允许程序执行NFC近距离通讯操作,用于移动支持
android.permission.PERSISTENT_ACTIVITY 永久Activity 创建一个永久的Activity,该功能标记为将来将被移除
android.permission.PROCESS_OUTGOING_CALLS 处理拨出电话 允许程序监视,修改或放弃播出电话
android.permission.READ_CALENDAR 读取日程提醒 允许程序读取用户的日程信息
android.permission.READ_CONTACTS 读取联系人 允许应用访问联系人通讯录信息
android.permission.READ_FRAME_BUFFER 屏幕截图 读取帧缓存用于屏幕截图
com.android.browser.permission.READ_HISTORY_BOOKMARKS 读取收藏夹和历史记录 读取浏览器收藏夹和历史记录
android.permission.READ_INPUT_STATE 读取输入状态 读取当前键的输入状态,仅用于系统
android.permission.READ_LOGS 读取系统日志 读取系统底层日志
android.permission.READ_PHONE_STATE 读取电话状态 访问电话状态
android.permission.READ_SMS 读取短信内容 读取短信内容
android.permission.READ_SYNC_SETTINGS 读取同步设置 读取同步设置,读取Google在线同步设置
android.permission.READ_SYNC_STATS 读取同步状态 读取同步状态,获得Google在线同步状态
android.permission.REBOOT 重启设备 允许程序重新启动设备
android.permission.RECEIVE_BOOT_COMPLETED 开机自动允许 允许程序开机自动运行
android.permission.RECEIVE_MMS 接收彩信 接收彩信
android.permission.RECEIVE_SMS 接收短信 接收短信
android.permission.RECEIVE_WAP_PUSH 接收Wap Push 接收WAP PUSH信息
android.permission.RECORD_AUDIO 录音 录制声音通过手机或耳机的麦克
android.permission.REORDER_TASKS 排序系统任务 重新排序系统Z轴运行中的任务
android.permission.RESTART_PACKAGES 结束系统任务 结束任务通过restartPackage(String)方法,该方式将在外来放弃
android.permission.SEND_SMS 发送短信 发送短信
android.permission.SET_ACTIVITY_WATCHER 设置Activity观察其 设置Activity观察器一般用于monkey测试
com.android.alarm.permission.SET_ALARM 设置闹铃提醒 设置闹铃提醒
android.permission.SET_ALWAYS_FINISH 设置总是退出 设置程序在后台是否总是退出
android.permission.SET_ANIMATION_SCALE 设置动画缩放 设置全局动画缩放
android.permission.SET_DEBUG_APP 设置调试程序 设置调试程序,一般用于开发
android.permission.SET_ORIENTATION 设置屏幕方向 设置屏幕方向为横屏或标准方式显示,不用于普通应用
android.permission.SET_PREFERRED_APPLICATIONS 设置应用参数 设置应用的参数,已不再工作具体查看addPackageToPreferred(String)介绍
android.permission.SET_PROCESS_LIMIT 设置进程限制 允许程序设置最大的进程数量的限制
android.permission.SET_TIME 设置系统时间 设置系统时间
android.permission.SET_TIME_ZONE 设置系统时区 设置系统时区
android.permission.SET_WALLPAPER 设置桌面壁纸 设置桌面壁纸
android.permission.SET_WALLPAPER_HINTS 设置壁纸建议 设置壁纸建议
android.permission.SIGNAL_PERSISTENT_PROCESSES 发送永久进程信号 发送一个永久的进程信号
android.permission.STATUS_BAR 状态栏控制 允许程序打开、关闭、禁用状态栏
android.permission.SUBSCRIBED_FEEDS_READ 访问订阅内容 访问订阅信息的数据库
android.permission.SUBSCRIBED_FEEDS_WRITE 写入订阅内容 写入或修改订阅内容的数据库
android.permission.SYSTEM_ALERT_WINDOW 显示系统窗口 显示系统窗口
android.permission.UPDATE_DEVICE_STATS 更新设备状态 更新设备状态
android.permission.USE_CREDENTIALS 使用证书 允许程序请求验证从AccountManager
android.permission.USE_SIP 使用SIP视频 允许程序使用SIP视频服务
android.permission.VIBRATE 使用振动 允许振动
android.permission.WAKE_LOCK 唤醒锁定 允许程序在手机屏幕关闭后后台进程仍然运行
android.permission.WRITE_APN_SETTINGS 写入GPRS接入点设置 写入网络GPRS接入点设置
android.permission.WRITE_CALENDAR 写入日程提醒 写入日程,但不可读取
android.permission.WRITE_CONTACTS 写系人 写系人,但不可读取
android.permission.WRITE_EXTERNAL_STORAGE 写入外部存储 允许程序写入外部存储,如SD卡上写文件
android.permission.WRITE_GSERVICES 写入Google地图数据 允许程序写入Google Map服务数据
com.android.browser.permission.WRITE_HISTORY_BOOKMARKS 写入收藏夹和历史记录 写入浏览器历史记录或收藏夹,但不可读取
android.permission.WRITE_SECURE_SETTINGS 读写系统敏感设置 允许程序读写系统安全敏感的设置项
android.permission.WRITE_SETTINGS 读写系统设置 允许读写系统设置项
android.permission.WRITE_SMS 编写短信 允许编写短信

收起阅读 »

#插件# IOS视频流处理插件需求外包

插件 iOS

1、 在IOS系统上实现:请求websocket接口,接口返回h264的fmp4数据流,把数据流渲染出来。
2、 原生实现,要求封装成uniapp的插件。
3、 兼容要求:
a) 兼容IOS9.0及以上版本IOS操作系统,兼容iPhone5S及以上型号iPhone手机;
详情见附件。联系电话或微信:13950003396

继续阅读 »

1、 在IOS系统上实现:请求websocket接口,接口返回h264的fmp4数据流,把数据流渲染出来。
2、 原生实现,要求封装成uniapp的插件。
3、 兼容要求:
a) 兼容IOS9.0及以上版本IOS操作系统,兼容iPhone5S及以上型号iPhone手机;
详情见附件。联系电话或微信:13950003396

收起阅读 »

有没有拼团买uniapp视频教程的,3人拼团只要1块钱,还差2个

uni-app 5小时快速入门【3人成团,快来加入】
https://m.imooc.com/groupdetail/1215/dpylDvPykRvXkBNm/Knx5yVlPmJY4g0R7?utm_source=share

uni-app 5小时快速入门【3人成团,快来加入】
https://m.imooc.com/groupdetail/1215/dpylDvPykRvXkBNm/Knx5yVlPmJY4g0R7?utm_source=share

uni-app 跨平台SDK 支持iOS端Android端,实时音视频快速接入

webrtc uniapp插件 SDK Android uni_app

uni-app 实时音视频快速接入

以Android平台为例,介绍一下uni-app原生插件

什么是uni原生插件

uni原生插件指的是将您本地原生开发的功能按照规范封装成插件包,然后即可在uni-app前端项目中通过js调用您开发的原生能力。

开发环境

详情请看原文

继续阅读 »

uni-app 实时音视频快速接入

以Android平台为例,介绍一下uni-app原生插件

什么是uni原生插件

uni原生插件指的是将您本地原生开发的功能按照规范封装成插件包,然后即可在uni-app前端项目中通过js调用您开发的原生能力。

开发环境

详情请看原文

收起阅读 »

Android uni-app 封装原生插件

视频 webrtc 直播 Android uniapp

前言

据广大用户的需求,需要把我们anyRTC的SDK,封装到uni-app来使用,并且实现音视频通话。这边文章图文讲解一下怎么封装原生插件,并且在下一章uni-app实现音视频通话。

anyRTC开发者中心

1.uni-app是什么?

1

一个使用Vue.js开发所有前端应用框架,开发者编写一套代码,可发布到ios、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。
如果你还没有了解uni-app是什么:点击这里:这篇文章举例说明。

2.uni-app的特点

1、跨平台更多

  • 真正做到“一套代码、多端发行”!
  • 条件编译:优雅的在一个项目里调用不同平台的特色功能!

2、运行体验好

  • 组件、api与微信小程序一致
  • 兼容weex原生渲染

3、通过技术栈,学习成本低

  • vue的语法、微信小程序的api
  • 内嵌mpvue

4、开放生态,组件更丰富

5、支持通过npm安装第三方包

6、支持微信小程序自定义组件及SDK

7、兼容mpvue组件及项目

8、App端支持和原生混合编码

9、DCloud将发布插件市场

3.uni-app封装原生插件

3.1 Android离线SDK下载:

点击下载,两个SDK均可。img

下载完成,解压备用:img

3.2 新建Android项目

打开Android Studio,建立一个No Activity项目。在菜单栏选择File>New>New Project

img

然后Next:填写项目名,包名,已经API Level。

img

然后Finish,为了更好的使用,我们把它转到Project视图。接下来创建开发的模块(Module)

点击File>New>New Module.

img

然后选择Android Library,点击Next:

img

自定义Library名 和包名,点击Finish

img

创建完毕视图如下:

img

接下来打开testplugin项目里的build.gradle,将原生的dependencies下默认生成的依赖注释掉,添加uni-app所需库的依赖:

compileOnly "com.android.support:recyclerview-v7:28.0.0"  
compileOnly "com.android.support:support-v4:28.0.0"  
compileOnly "com.android.support:appcompat-v7:28.0.0"  
implementation 'com.alibaba:fastjson:1.1.46.android'  
implementation 'com.facebook.fresco:fresco:1.13.0'

添加完成如下图所示:img

另外把导入的uniapp-release.aar插件,它是扩展module主要依赖库

img

回到刚刚创建的testplugin的build.gradle中,接下来进行导入aar需要的配置操作:

添加: 放在android{}外

repositories {  
    flatDir {  
        dirs 'libs'  
    }  
}  

然后在dependencies内添加:

compileOnly fileTree(dir: '../app/libs', include: ['uniapp-release.aar'])  

添加完毕,如下图:

img

然后 同步 Sync Now!

3.3 原生插件的开发

以扩展Module为例,如图创建类TestModule:

img

Module扩展注意事项:

img

写一个小demo:

import android.util.Log;  
import com.alibaba.fastjson.JSONObject;  
import com.taobao.weex.annotation.JSMethod;  
import com.taobao.weex.bridge.JSCallback;  
import com.taobao.weex.common.WXModule;  

public class TestModule extends WXModule{  
    String NAME="name";  
    String AGE ="age";  

    @JSMethod(uiThread = true)  
    public void testText(JSONObject options, JSCallback callBack){  
        Log.e("TestModule", "成功调用!" );  
        String name =options.getString(NAME);  
        String age =options.getString(AGE);  
        JSONObject data =new JSONObject();  
        if (name !=null && !name.isEmpty() && age !=null && !age.isEmpty()){  
            int _age =Integer.parseInt(age);  
            if (_age<0 || _age>30){  
                data.put("code","不合格!");  
            }else {  
                age=(_age>0 && _age<10) ? "0"+age:age;  
                data.put("code","合格:"+"姓名_"+name+",年龄_"+age);  
            }  
        }else {  
            data.put("code","输入无效!");  
        }  
        callBack.invoke(data);  
    }  
}  

3.3.1 注册插件:

方法(一)

app>src>main目录下创建assets文件夹

img

img

img

app>src>main>assets目录下创建 dcloud_uniplugins.json 文件,然后添加:

{  
  "nativePlugins": [  
    {  
      "plugins": [  
        {  
          "type": "module",  
          "name": "Test-Module",  
          "class": "com.test.testplugin.TestModule"  
        }  
      ]  
    }  
  ]  
}  

方法(二)

对创建的Module扩展testplugin进行操作,在 src>main>java>插件包名(这里是com.test.testplugin)目录下创建类TestModule_AppProxy

img

img

TestModule_AppProxy类要实现AppHookProxy接口,在onCreate()方法中添加weex注册相关参数或填写插件需要在启动时初始化的逻辑。

img

hooksClass节点填入你创建的实现AppHookProxy接口的实体类的完整名称 (注:有些需要初始化操作的需求可以在此处添加逻辑,无特殊操作仅使用第一种方式注册即可无需集成AppHookProxy接口)

img

注册完毕,开始打包插件

3.3.2 打包插件:

在Gradle>testplugin>Tasks>other目录下找到assembleRelease,双击等待系统编译出扩展module的aar文件

注意:官方文档中是

选择

Gradle--->插件module--->Tasks--->build--->assembleRelease编译module的aar文件

,在新版本的AndroidStudio中,assembleReleaseassembleDebug被转移到other目录下。

img

img

img

img

成功后在testplugin>build>outputs>aar目录下就可以找到相关插件了

img

4.HBuilderX导入和使用本地插件

创建uni-app默认项目TestModule

img

参照官方文档中的目录规范,将刚才打包的插件放到nativeplugins>插件文件夹名称(我的是Test-Module)>android目录下,没有相关目录就一步步创建。

img

创建package.json——uni原生插件描述文件,放到插件文件夹名称目录下,与android文件夹并列

img

注意:插件标识id必须在对应android和ios节点下plugins中进行注册,与name字段值一致。name下的class是注册插件的类名,也要填对。

这里因为只有android插件,就把ios节点全部删掉,在这里直接注释的话是无效的。

img

还有要注意的一点:插件标识id一定要与插件文件夹名称一致,不然在云打包时会提示 插件不合法:该插件在nativePlugins目录下不存在。

在manifest.json下配置App原生插件

img

勾选并确认

img

parameters信息根据需求配置

img

接下来在HBuilderX内对项目中的index.vue文件(在pages>index目录下)做出一定更改,以便后续测试开发的原生插件。

<template>  
    <view class="container">  
        姓名<input v-model="name" placeholder="点此编辑姓名" >  
        <br>  
        年龄<input v-model="age" placeholder="点此编辑年龄" >  
        <br>  
        <button type="default" @click="test">提交</button>  
        <view>{{name}}</view>  
        <view>{{age}}</view>  
        <view>{{ result }}</view>  
    </view>  
</template>  

<script>  
    var testModule = uni.requireNativePlugin("Test-Module");  
    export default {  
        data() {  
            return {  
                name:"",  
                age:"",  
                result:"暂未提交"  
            }  
        },  
        methods: {  
            test(){  
                testModule.testText({  
                    'name':this.name,  
                    'age':this.age,  
                },  
                (ret) =>{  
                    this.result="[提交反馈]"+ret.code;  
                })  
            }  
        }  
    }  
</script>  

img

5.运行项目

打包自定义基座:

运行(R)>运行到手机或模拟器(N)>制作自定义调试基座(P)

img

配置App云端打包信息:

img

云端打包:

img

提交到云端服务器:

img

打包成功:

img

打包成功后需要在下图位置确保开启自定义调试基座功能:

img

img

启动模拟器,或者真机运行:

img

进入测试项目:

img

输入姓名tom和年龄9(大于0小于10前面自动补0)测试插件,点击提交

img

反馈提交结果

img

把年龄改为31(插件设置年龄范围为0~30),反馈如下

img

至此,测试成功!

完结!

作者:anyRTC-东慕雨
anyRTC官网:https://www.anyrtc.io/
链接:https://juejin.cn/post/6904902110505402376

继续阅读 »

前言

据广大用户的需求,需要把我们anyRTC的SDK,封装到uni-app来使用,并且实现音视频通话。这边文章图文讲解一下怎么封装原生插件,并且在下一章uni-app实现音视频通话。

anyRTC开发者中心

1.uni-app是什么?

1

一个使用Vue.js开发所有前端应用框架,开发者编写一套代码,可发布到ios、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。
如果你还没有了解uni-app是什么:点击这里:这篇文章举例说明。

2.uni-app的特点

1、跨平台更多

  • 真正做到“一套代码、多端发行”!
  • 条件编译:优雅的在一个项目里调用不同平台的特色功能!

2、运行体验好

  • 组件、api与微信小程序一致
  • 兼容weex原生渲染

3、通过技术栈,学习成本低

  • vue的语法、微信小程序的api
  • 内嵌mpvue

4、开放生态,组件更丰富

5、支持通过npm安装第三方包

6、支持微信小程序自定义组件及SDK

7、兼容mpvue组件及项目

8、App端支持和原生混合编码

9、DCloud将发布插件市场

3.uni-app封装原生插件

3.1 Android离线SDK下载:

点击下载,两个SDK均可。img

下载完成,解压备用:img

3.2 新建Android项目

打开Android Studio,建立一个No Activity项目。在菜单栏选择File>New>New Project

img

然后Next:填写项目名,包名,已经API Level。

img

然后Finish,为了更好的使用,我们把它转到Project视图。接下来创建开发的模块(Module)

点击File>New>New Module.

img

然后选择Android Library,点击Next:

img

自定义Library名 和包名,点击Finish

img

创建完毕视图如下:

img

接下来打开testplugin项目里的build.gradle,将原生的dependencies下默认生成的依赖注释掉,添加uni-app所需库的依赖:

compileOnly "com.android.support:recyclerview-v7:28.0.0"  
compileOnly "com.android.support:support-v4:28.0.0"  
compileOnly "com.android.support:appcompat-v7:28.0.0"  
implementation 'com.alibaba:fastjson:1.1.46.android'  
implementation 'com.facebook.fresco:fresco:1.13.0'

添加完成如下图所示:img

另外把导入的uniapp-release.aar插件,它是扩展module主要依赖库

img

回到刚刚创建的testplugin的build.gradle中,接下来进行导入aar需要的配置操作:

添加: 放在android{}外

repositories {  
    flatDir {  
        dirs 'libs'  
    }  
}  

然后在dependencies内添加:

compileOnly fileTree(dir: '../app/libs', include: ['uniapp-release.aar'])  

添加完毕,如下图:

img

然后 同步 Sync Now!

3.3 原生插件的开发

以扩展Module为例,如图创建类TestModule:

img

Module扩展注意事项:

img

写一个小demo:

import android.util.Log;  
import com.alibaba.fastjson.JSONObject;  
import com.taobao.weex.annotation.JSMethod;  
import com.taobao.weex.bridge.JSCallback;  
import com.taobao.weex.common.WXModule;  

public class TestModule extends WXModule{  
    String NAME="name";  
    String AGE ="age";  

    @JSMethod(uiThread = true)  
    public void testText(JSONObject options, JSCallback callBack){  
        Log.e("TestModule", "成功调用!" );  
        String name =options.getString(NAME);  
        String age =options.getString(AGE);  
        JSONObject data =new JSONObject();  
        if (name !=null && !name.isEmpty() && age !=null && !age.isEmpty()){  
            int _age =Integer.parseInt(age);  
            if (_age<0 || _age>30){  
                data.put("code","不合格!");  
            }else {  
                age=(_age>0 && _age<10) ? "0"+age:age;  
                data.put("code","合格:"+"姓名_"+name+",年龄_"+age);  
            }  
        }else {  
            data.put("code","输入无效!");  
        }  
        callBack.invoke(data);  
    }  
}  

3.3.1 注册插件:

方法(一)

app>src>main目录下创建assets文件夹

img

img

img

app>src>main>assets目录下创建 dcloud_uniplugins.json 文件,然后添加:

{  
  "nativePlugins": [  
    {  
      "plugins": [  
        {  
          "type": "module",  
          "name": "Test-Module",  
          "class": "com.test.testplugin.TestModule"  
        }  
      ]  
    }  
  ]  
}  

方法(二)

对创建的Module扩展testplugin进行操作,在 src>main>java>插件包名(这里是com.test.testplugin)目录下创建类TestModule_AppProxy

img

img

TestModule_AppProxy类要实现AppHookProxy接口,在onCreate()方法中添加weex注册相关参数或填写插件需要在启动时初始化的逻辑。

img

hooksClass节点填入你创建的实现AppHookProxy接口的实体类的完整名称 (注:有些需要初始化操作的需求可以在此处添加逻辑,无特殊操作仅使用第一种方式注册即可无需集成AppHookProxy接口)

img

注册完毕,开始打包插件

3.3.2 打包插件:

在Gradle>testplugin>Tasks>other目录下找到assembleRelease,双击等待系统编译出扩展module的aar文件

注意:官方文档中是

选择

Gradle--->插件module--->Tasks--->build--->assembleRelease编译module的aar文件

,在新版本的AndroidStudio中,assembleReleaseassembleDebug被转移到other目录下。

img

img

img

img

成功后在testplugin>build>outputs>aar目录下就可以找到相关插件了

img

4.HBuilderX导入和使用本地插件

创建uni-app默认项目TestModule

img

参照官方文档中的目录规范,将刚才打包的插件放到nativeplugins>插件文件夹名称(我的是Test-Module)>android目录下,没有相关目录就一步步创建。

img

创建package.json——uni原生插件描述文件,放到插件文件夹名称目录下,与android文件夹并列

img

注意:插件标识id必须在对应android和ios节点下plugins中进行注册,与name字段值一致。name下的class是注册插件的类名,也要填对。

这里因为只有android插件,就把ios节点全部删掉,在这里直接注释的话是无效的。

img

还有要注意的一点:插件标识id一定要与插件文件夹名称一致,不然在云打包时会提示 插件不合法:该插件在nativePlugins目录下不存在。

在manifest.json下配置App原生插件

img

勾选并确认

img

parameters信息根据需求配置

img

接下来在HBuilderX内对项目中的index.vue文件(在pages>index目录下)做出一定更改,以便后续测试开发的原生插件。

<template>  
    <view class="container">  
        姓名<input v-model="name" placeholder="点此编辑姓名" >  
        <br>  
        年龄<input v-model="age" placeholder="点此编辑年龄" >  
        <br>  
        <button type="default" @click="test">提交</button>  
        <view>{{name}}</view>  
        <view>{{age}}</view>  
        <view>{{ result }}</view>  
    </view>  
</template>  

<script>  
    var testModule = uni.requireNativePlugin("Test-Module");  
    export default {  
        data() {  
            return {  
                name:"",  
                age:"",  
                result:"暂未提交"  
            }  
        },  
        methods: {  
            test(){  
                testModule.testText({  
                    'name':this.name,  
                    'age':this.age,  
                },  
                (ret) =>{  
                    this.result="[提交反馈]"+ret.code;  
                })  
            }  
        }  
    }  
</script>  

img

5.运行项目

打包自定义基座:

运行(R)>运行到手机或模拟器(N)>制作自定义调试基座(P)

img

配置App云端打包信息:

img

云端打包:

img

提交到云端服务器:

img

打包成功:

img

打包成功后需要在下图位置确保开启自定义调试基座功能:

img

img

启动模拟器,或者真机运行:

img

进入测试项目:

img

输入姓名tom和年龄9(大于0小于10前面自动补0)测试插件,点击提交

img

反馈提交结果

img

把年龄改为31(插件设置年龄范围为0~30),反馈如下

img

至此,测试成功!

完结!

作者:anyRTC-东慕雨
anyRTC官网:https://www.anyrtc.io/
链接:https://juejin.cn/post/6904902110505402376

收起阅读 »

Android uni-app 封装原生插件

直播 SDK Android webrtc uniapp

前言

据广大用户的需求,需要把我们anyRTC的SDK,封装到uni-app来使用,并且实现音视频通话。这边文章图文讲解一下怎么封装原生插件,并且在下一章uni-app实现音视频通话。

后续文章内容请看原文链接

作者:anyRTC-东慕雨
anyRTC官网:https://www.anyrtc.io/

继续阅读 »

前言

据广大用户的需求,需要把我们anyRTC的SDK,封装到uni-app来使用,并且实现音视频通话。这边文章图文讲解一下怎么封装原生插件,并且在下一章uni-app实现音视频通话。

后续文章内容请看原文链接

作者:anyRTC-东慕雨
anyRTC官网:https://www.anyrtc.io/

收起阅读 »