HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

匠心出品,跨平台前端组件库FirstUI 1.2.0正式发布

FirstUI(https://www.firstui.cn/)是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。FirstUI开发者、设计师不断精心打磨,持续发布新的组件、模板等新功能,力求为用户提供更高品质的产品,节约用户时间与成本。

一、FirstUI特性

● 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
● 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
● 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
● 专属社区。我们打造了FirstU专属社区,用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。

二、FirstUI体系进展

三、扫码体验FirstUI

考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。

四、开源版与商业版

FirstUI分为开源版与商业版,部分组件为商业版专属使用。

1、开源版

● github: https://github.com/FirstUI/FirstUI (欢迎star :-D)
● gitee: https://gitee.com/firstui/FirstUI (欢迎star :-D)
● 文档地址: https://doc.firstui.cn

2、VIP会员权益:

● 完整版源码
● 全部组件
● 物料商城享VIP折扣
● 专属会员群指导、答疑
● 新特性优先体验
● VIP专属文档
会员权益详情: https://www.firstui.cn/right

3、新版优惠

新版发布,优惠期内框架可5折¥150元购买(原价¥300元),2022年01月31日截止。购买框架产品即升级为VIP会员,享受VIP会员权益。
立即购买:https://www.firstui.cn/store/detail/1

继续阅读 »

FirstUI(https://www.firstui.cn/)是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。FirstUI开发者、设计师不断精心打磨,持续发布新的组件、模板等新功能,力求为用户提供更高品质的产品,节约用户时间与成本。

一、FirstUI特性

● 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
● 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
● 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
● 专属社区。我们打造了FirstU专属社区,用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。

二、FirstUI体系进展

三、扫码体验FirstUI

考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。

四、开源版与商业版

FirstUI分为开源版与商业版,部分组件为商业版专属使用。

1、开源版

● github: https://github.com/FirstUI/FirstUI (欢迎star :-D)
● gitee: https://gitee.com/firstui/FirstUI (欢迎star :-D)
● 文档地址: https://doc.firstui.cn

2、VIP会员权益:

● 完整版源码
● 全部组件
● 物料商城享VIP折扣
● 专属会员群指导、答疑
● 新特性优先体验
● VIP专属文档
会员权益详情: https://www.firstui.cn/right

3、新版优惠

新版发布,优惠期内框架可5折¥150元购买(原价¥300元),2022年01月31日截止。购买框架产品即升级为VIP会员,享受VIP会员权益。
立即购买:https://www.firstui.cn/store/detail/1

收起阅读 »

说真的。文档体验绝了

文档

说真的。文档体验绝了,真的绝了。

我们用开源的东西本不该开口索要的,uni-app也算是你们的一个产品了。 你们也有商业模式,能否将文档好好整理一下, 文档体验简直不要太好

说真的。文档体验绝了,真的绝了。

我们用开源的东西本不该开口索要的,uni-app也算是你们的一个产品了。 你们也有商业模式,能否将文档好好整理一下, 文档体验简直不要太好

tag的监听事件方法。

代码渲染的titleNView 里的tag如何监听。代码如下。
假设你的tags里有一个子tag left 距离是30,宽度是130。
//那么就先定义监听区域
plus.webview.currentWebview().getTitleNView().setTouchEventRect({top:'0px',left:'30px',width:'130px',height:'100%'})
//创建监听。
plus.webview.currentWebview().getTitleNView().addEventListener("click",function(e){ })

注:setTouchEventRect 可以传数组。多个区域监听。
有问题欢迎留言,大家一起讨论。

继续阅读 »

代码渲染的titleNView 里的tag如何监听。代码如下。
假设你的tags里有一个子tag left 距离是30,宽度是130。
//那么就先定义监听区域
plus.webview.currentWebview().getTitleNView().setTouchEventRect({top:'0px',left:'30px',width:'130px',height:'100%'})
//创建监听。
plus.webview.currentWebview().getTitleNView().addEventListener("click",function(e){ })

注:setTouchEventRect 可以传数组。多个区域监听。
有问题欢迎留言,大家一起讨论。

收起阅读 »

3.2.16.20211122 无法打包h5

h5

我回退了版本就可以了

我回退了版本就可以了

uniapp直接跳转应用商店

// 跳转应用商店
export const jumpToAppMarket = (code) => {
// 可以根據文檔換成其他商店 https://www.jianshu.com/p/b544810beac3
const googlePlay = "com.android.vending";

if (plus.os.name == "Android") {  
    var Uri = plus.android.importClass("android.net.Uri");  
    var Intent = plus.android.importClass('android.content.Intent');  
    var main = plus.android.runtimeMainActivity();  

    var uri = Uri.parse("market://details?id=" + 包名);  
    var intent = new Intent(Intent.ACTION_VIEW, uri);  
    // 选择进入商店  
    intent.setPackage(googlePlay);  
    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK;  
    // 没有该商店应用  
    if (intent.resolveActivity(main.getPackageManager()) !== null) {  
        main.startActivity(intent);  
    } else {  
        // 跳转浏览器  
        let uri = Uri.parse("https://play.google.com/store/apps/details?id=" + 包名);  
        let intent = new Intent(Intent.ACTION_VIEW, uri);  
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK;  
        intent.setPackage('com.android.browser');  
        main.startActivity(intent);  
    }  
} else {  
    plus.runtime.openURL('itms-apps://itunes.apple.com/cn/app/id{appid}?mt=8');  
}  

}

继续阅读 »

// 跳转应用商店
export const jumpToAppMarket = (code) => {
// 可以根據文檔換成其他商店 https://www.jianshu.com/p/b544810beac3
const googlePlay = "com.android.vending";

if (plus.os.name == "Android") {  
    var Uri = plus.android.importClass("android.net.Uri");  
    var Intent = plus.android.importClass('android.content.Intent');  
    var main = plus.android.runtimeMainActivity();  

    var uri = Uri.parse("market://details?id=" + 包名);  
    var intent = new Intent(Intent.ACTION_VIEW, uri);  
    // 选择进入商店  
    intent.setPackage(googlePlay);  
    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK;  
    // 没有该商店应用  
    if (intent.resolveActivity(main.getPackageManager()) !== null) {  
        main.startActivity(intent);  
    } else {  
        // 跳转浏览器  
        let uri = Uri.parse("https://play.google.com/store/apps/details?id=" + 包名);  
        let intent = new Intent(Intent.ACTION_VIEW, uri);  
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK;  
        intent.setPackage('com.android.browser');  
        main.startActivity(intent);  
    }  
} else {  
    plus.runtime.openURL('itms-apps://itunes.apple.com/cn/app/id{appid}?mt=8');  
}  

}

收起阅读 »

基于uniapp打包生成app,并引入本地原生插件步骤和踩坑

uniapp原生插件

看到社区很多同学对于uniapp和原生混合开发配置问题,就怎么引入原生本地插件并配置运行步骤详解
1.首先在你的文件夹下建nativeplugins目录,将本地开发的原生插件代码包按照格式放入

2.对nativeplugins目录下的pack.json进行配置 注意name和id要与目录名保持一致,class要安卓小伙伴给你,包名 类名

3.在manifest.json进行插件引入

第四步:点击运行到手机或模拟器,先制定基座,制作完成后在运行基座选择自定义基座,进行打包即可

继续阅读 »

看到社区很多同学对于uniapp和原生混合开发配置问题,就怎么引入原生本地插件并配置运行步骤详解
1.首先在你的文件夹下建nativeplugins目录,将本地开发的原生插件代码包按照格式放入

2.对nativeplugins目录下的pack.json进行配置 注意name和id要与目录名保持一致,class要安卓小伙伴给你,包名 类名

3.在manifest.json进行插件引入

第四步:点击运行到手机或模拟器,先制定基座,制作完成后在运行基座选择自定义基座,进行打包即可

收起阅读 »

录音支持暂停、继续、后台录音、息屏录音(ios、andorid)

录音支持暂停、继续、后台录音、息屏录音(ios、andorid) :https://ext.dcloud.net.cn/plugin?id=5849

录音支持暂停、继续、后台录音、息屏录音(ios、andorid) :https://ext.dcloud.net.cn/plugin?id=5849

强制安卓进入前台运行,完整代码及遇到的坑

安卓 uniapp原生插件

​最近突发奇想,想做一个给自己用的app,主要时做一些个人记录和强制停止无脑刷屏==。
在每次解锁手机或刷视频超过一定时间,后台就把自己开发的app进入前台,提醒自己的目标及可以做其他的事有什么。
网上的资料并不能完整的解决问题,然后就自己撸了一下代码。
todo 之后有空的话把这部分打包成一个uniapp的插件。

完整代码

    Timer topTimer=null;  

    public void startTopTimer(){  
        stopTopTimer();  
        topTimer=new Timer();  
        TimerTask timerTask=new TimerTask() {  
            @Override  
            public void run() {  
                activityInTop();  
            }  
        };  
        topTimer.schedule(timerTask,0,500);  
    }  

    public void stopTopTimer(){  
        if(topTimer!=null){  
            topTimer.cancel();  
            topTimer=null;  
        }  
    }  

    public void activityInTop() {  
        /**获取ActivityManager*/  
        ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(ACTIVITY_SERVICE);  

        /**获得当前运行的task(任务)*/  
        int nowPosition=0;  
        List<ActivityManager.RunningTaskInfo> taskInfoList = activityManager.getRunningTasks(100);  
        for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {  
            /**找到本应用的 task,并将它切换到前台*/  
            if (taskInfo.topActivity.getPackageName().equals(getApplicationContext().getPackageName())) {  
                if(nowPosition==0){  
                    stopTopTimer();  
                    return;  
                }  
                activityManager.moveTaskToFront(taskInfo.id, 0);  
                Log.d(TAG, "找到本应用的 task,并将它切换到前台");  
                return;  
            }  
            nowPosition++;  
        }  
        // 应用需要置顶且前台应用被关闭时,重新打开应用  
        Intent intent=getPackageManager().getLaunchIntentForPackage(getPackageName());  
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        startActivity(intent);  
    }

使用

startTopTimer();

碰到的坑

应用被关闭时,service需要拉起一个新的应用

// 应用需要置顶且前台应用被关闭时,重新打开应用  
        Intent intent=getPackageManager().getLaunchIntentForPackage(getPackageName());  
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        startActivity(intent);

moveTaskToFront需要在应用进入后台5-7s后执行

解决方案:执行moveTaskToFront方法后,判断是否成功。不成功每间隔500ms再执行一次,直到成功为止。

继续阅读 »

​最近突发奇想,想做一个给自己用的app,主要时做一些个人记录和强制停止无脑刷屏==。
在每次解锁手机或刷视频超过一定时间,后台就把自己开发的app进入前台,提醒自己的目标及可以做其他的事有什么。
网上的资料并不能完整的解决问题,然后就自己撸了一下代码。
todo 之后有空的话把这部分打包成一个uniapp的插件。

完整代码

    Timer topTimer=null;  

    public void startTopTimer(){  
        stopTopTimer();  
        topTimer=new Timer();  
        TimerTask timerTask=new TimerTask() {  
            @Override  
            public void run() {  
                activityInTop();  
            }  
        };  
        topTimer.schedule(timerTask,0,500);  
    }  

    public void stopTopTimer(){  
        if(topTimer!=null){  
            topTimer.cancel();  
            topTimer=null;  
        }  
    }  

    public void activityInTop() {  
        /**获取ActivityManager*/  
        ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(ACTIVITY_SERVICE);  

        /**获得当前运行的task(任务)*/  
        int nowPosition=0;  
        List<ActivityManager.RunningTaskInfo> taskInfoList = activityManager.getRunningTasks(100);  
        for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {  
            /**找到本应用的 task,并将它切换到前台*/  
            if (taskInfo.topActivity.getPackageName().equals(getApplicationContext().getPackageName())) {  
                if(nowPosition==0){  
                    stopTopTimer();  
                    return;  
                }  
                activityManager.moveTaskToFront(taskInfo.id, 0);  
                Log.d(TAG, "找到本应用的 task,并将它切换到前台");  
                return;  
            }  
            nowPosition++;  
        }  
        // 应用需要置顶且前台应用被关闭时,重新打开应用  
        Intent intent=getPackageManager().getLaunchIntentForPackage(getPackageName());  
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        startActivity(intent);  
    }

使用

startTopTimer();

碰到的坑

应用被关闭时,service需要拉起一个新的应用

// 应用需要置顶且前台应用被关闭时,重新打开应用  
        Intent intent=getPackageManager().getLaunchIntentForPackage(getPackageName());  
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        startActivity(intent);

moveTaskToFront需要在应用进入后台5-7s后执行

解决方案:执行moveTaskToFront方法后,判断是否成功。不成功每间隔500ms再执行一次,直到成功为止。

收起阅读 »

开发境外软件,需要调用谷歌地图

开发境外软件,需要调用谷歌地图

开发境外软件,需要调用谷歌地图

Android 音乐通知栏Native.js版本!

Native.JS uni_app

代码

const androidNotifi = {  
  NotifyID: 237,  
  receiver:null,  
  NotifiService:null,  
  serv: null,  
  intent: null,  
  mNotificationBuild: null,  
  mediaBtn: null,  
  channelID : '1',  
  channelName : '音乐通知',  
  notifiImg:{},  
  getImgTemp: function(audioImage) {  
    return new Promise(resolve => {  
      if (this.notifiImg[encodeURIComponent(audioImage)]) {  
        return resolve(plus.io.convertLocalFileSystemURL(this.notifiImg[encodeURIComponent(audioImage)]));  
      }  
      if(!/^http/.test(audioImage)) {  
        this.notifiImg[encodeURIComponent(audioImage)] = audioImage ? audioImage : '_www/static/image/notifimusic.png';  
        return resolve(plus.io.convertLocalFileSystemURL(audioImage ? audioImage : '_www/static/image/notifimusic.png'));  
      }  
      uni.downloadFile({  
        url: audioImage,  
        complete: res => {  
          this.notifiImg[encodeURIComponent(audioImage)] = res.tempFilePath ? res.tempFilePath : '_www/static/image/notifimusic.png';  
          return resolve(plus.io.convertLocalFileSystemURL(res.tempFilePath ? res.tempFilePath : '_www/static/image/notifimusic.png'));  
        }  
      });  
    });  
  },  
  hideNotifi: function(server = false){  
    this.main&&this.receiver&&this.main.unregisterReceiver(this.receiver);   
    server&&this.serv&&this.serv.stopForeground&&this.serv.stopForeground(this.NotifyID, this.mNotificationBuild) ;  
    if (Number(plus.os.version.split('.')[0])>= 8){     
      this.NotifiService&&this.NotifiService.cancel&&this.NotifiService.cancel(this.channelID,this.NotifyID);   
    } else {      
      this.NotifiService&&this.NotifiService.cancel&&this.NotifiService.cancel(this.NotifyID);   

    }   
    this.receiver = null;  
    this.NotifiService = null;  
    this.serv = null;  
    this.intent = null;  
    this.mNotificationBuild = null;  
    // this.NotifiService&&this.NotifiService.cancelAll&&this.NotifiService.cancelAll();  
    this.mediaBtn ? this.mediaBtn.release() : ''; // 线控销毁  
  },  
  showNotifi:async function(param) {  
    if (uni.$_sko.systemInfo.platform != 'android') {   
      return;  
    }  
    try{  
      param = Object.assign({  
        title : '通知标题',  
        subTitle: '',  
        cont : '通知内容',  
        isPlay: true, // 媒体播放按钮  
        legIcon : plus.io.convertLocalFileSystemURL('_www/static/image/notifimusic.png'),  
        Ongoing : false,    // 不可删除  
      },param);  
      this.main = this.main ? this.main : plus.android.runtimeMainActivity();    
      var Context = plus.android.importClass("android.content.Context");    
      var BitmapFactory = plus.android.importClass("android.graphics.BitmapFactory");    
      var NotificationManager = plus.android.importClass("android.app.NotificationManager");  
      var Notification = plus.android.importClass("android.app.Notification");;    
      var Intent = plus.android.importClass("android.content.Intent");    
      var PendingIntent = plus.android.importClass("android.app.PendingIntent");    
      var androidR = plus.android.importClass("android.R");    
      this.NotifiService = this.NotifiService ? this.NotifiService : this.main.getSystemService(Context.NOTIFICATION_SERVICE);    
      this.intent = this.intent ? this.intent : new Intent(this.main, this.main.getClass());    
      var UNI_MEDIA_BROAD = this.main.getPackageName() + '.mediaBtn';  

      //PendingIntent.getActivity的第二个参数需要设置为随机数,否则多个通知时会导致前面的通知被后面的通知替换Extra的数据    
      var pendingIntent = PendingIntent.getActivity(this.main, 0, this.intent, PendingIntent.FLAG_CANCEL_CURRENT);    
      var firstVersionNumber = Number(plus.os.version.split('.')[0]);    
      var mNotification;    
       //判断当前系统版本在8.0及以上    
      if (firstVersionNumber >= 8){  
          if (this.NotifiService.getNotificationChannel() == null){    
              var NotificationChannel = plus.android.importClass('android.app.NotificationChannel');    
              var channel = new NotificationChannel(this.channelID, this.channelName, NotificationManager.IMPORTANCE_HIGH);      
              this.NotifiService.createNotificationChannel(channel);      
              plus.android.autoCollection(NotificationChannel);  
          }    
          param.callBack&&this.main.startForegroundService(this.intent);  
          mNotification = new Notification.Builder(this.main, this.channelID);    
      } else {      
          mNotification = new Notification.Builder(this.main);      
          param.callBack&&this.main.startService(this.intent);  
      }      

      if(param.callBack) {  
        mNotification.addAction(androidR.drawable.ic_media_previous, "prev", PendingIntent.getBroadcast(this.main, 0, new Intent().setAction(UNI_MEDIA_BROAD + '.prev'), PendingIntent.FLAG_CANCEL_CURRENT));  
        mNotification.addAction((param.isPlay ? androidR.drawable.ic_media_pause : androidR.drawable.ic_media_play), "play", PendingIntent.getBroadcast(this.main, 0, new Intent().setAction(UNI_MEDIA_BROAD + '.play'), PendingIntent.FLAG_CANCEL_CURRENT));  
        mNotification.addAction(androidR.drawable.ic_media_next, "next", PendingIntent.getBroadcast(this.main, 0, new Intent().setAction(UNI_MEDIA_BROAD + '.next'), PendingIntent.FLAG_CANCEL_CURRENT));  
        var mMediaStyle = new Notification.MediaStyle();  
        mMediaStyle.setShowActionsInCompactView(0, 1, 2);  
        mNotification.setStyle(mMediaStyle);  
      }  
      mNotification.setOngoing(param.Ongoing);        
      param.legIcon&&mNotification.setLargeIcon(BitmapFactory.decodeFile(await this.getImgTemp(param.legIcon)));  
       mNotification.setSmallIcon(param.callBack?androidR.drawable.stat_sys_headset:androidR.drawable.stat_notify_chat); //设置图标    
       mNotification.setContentTitle(param.title);//设置标题  
       mNotification.setContentText(param.cont); //设置内容    
       param.subTitle&&mNotification.setSubText(param.subTitle);        //子内容暂时去掉    
       // mNotification.setAutoCancel(true);       //设置点击消失    
       //  mNotification.setShowWhen(true);         //显示通知时间    
       mNotification.setTicker("PadInfo");       //弹出通知    
       mNotification.setDefaults(Notification.DEFAULT_ALL);    
       // mNotification.setPriority(Notification.PRIORITY_DEFAULT);   //通知优先级      
       // mNotification.flags=Notification.FLAG_ONLY_ALERT_ONCE;     //发起通知时震动      
       mNotification.setContentIntent(pendingIntent);      

       this.mNotificationBuild = mNotification.build();  
       if (firstVersionNumber>= 8){      
         this.NotifiService.notify(this.channelID, this.NotifyID, this.mNotificationBuild);        
       } else {      
         this.NotifiService.notify(this.NotifyID, this.mNotificationBuild);      
       }   
       if(param.callBack) {  
          this.main&&this.receiver&&this.main.unregisterReceiver(this.receiver);   
          this.receiver = this.receiver ? this.receiver : plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {    
            onReceive: function(context, intent) { //实现onReceiver回调函数    
              param.callBack(intent.getAction().replace(UNI_MEDIA_BROAD + '.',''));    
            }    
          });   
        var IntentFilter = plus.android.importClass('android.content.IntentFilter');    
        var filter = new IntentFilter();    
        filter.addAction(UNI_MEDIA_BROAD + '.prev');  
        filter.addAction(UNI_MEDIA_BROAD + '.play');  
        filter.addAction(UNI_MEDIA_BROAD + '.next');  
        this.main.unregisterReceiver(this.receiver);  
        this.main.registerReceiver(this.receiver, filter); //注册监听    
        this.mediaBtn = uni.requireNativePlugin('uniMediaButton');  // 监听线控  需要配合附件中的插件( Android API >= 26)  
        this.mediaBtn.initMediaButton(res => {  
           console.log(res.message);  
          // 绑定失败/销毁  
          if(res.code == '-1') {  
            this.mediaBtn = null;  
            return;  
          }  
           // res.keyAction == 1 按键松开 res.keyAction == 0 按键按下 播放按钮肯能只有按下  
          if(res.keyCode && res.keyAction == '0') {  
            switch (res.keyCode){  
              case 88:  
                param.callBack(  
                  'prev'  
                );  
                break;  
              case 87:  
                param.callBack(  
                  'next'  
                );  
                break;  
              case 127:  
              case 126:  
                param.callBack(  
                  'play'  
                );  
                break;  
            }  
          }  
        });  
        var serv = plus.android.importClass("android.app.Service");  
        this.serv = this.serv ? this.serv : new serv();  
        this.serv.startForeground(this.NotifyID, this.mNotificationBuild);  
        plus.android.autoCollection(serv);  
        plus.android.autoCollection(IntentFilter);  
       }  
      plus.android.autoCollection(Context);  
      plus.android.autoCollection(BitmapFactory);  
      plus.android.autoCollection(NotificationManager);  
      plus.android.autoCollection(Notification);  
      plus.android.autoCollection(Intent);  
      plus.android.autoCollection(PendingIntent);  
      plus.android.autoCollection(androidR);  
      plus.android.autoCollection(this.receiver);  

    }catch(e){  
      console.log(e);  
    }  
  }  
};

使用方法

// 开启 - 更新标题/播放(暂停)按钮  
show();  
function show(){  
    androidNotifi.showNotifi({  
        title : '通知标题',  
        subTitle: '通知小标题',  
        cont : '通知内容',  
        isPlay: true, // 媒体播放按钮状态(控制播放/暂停按钮样式)  
        legIcon : '', // 封面图片  
        Ongoing : false,    // 是否可删除  
        callBack:function(res){  
            switch (res){  
            case 'play':  
        console.log('play');  
        break;  
        case 'prev':  
        console.log('prev');  
        break;  
        case 'next':  
        console.log('next');  
        break;  
        }  
        }  
    })  
}  
// 关闭  
function hide(){  
    androidNotifi.hideNotifi();  
}  

由于Android播放视频没有通知栏控制.开始一直使用XZH-musicNotification 的插件,但是由于版本更新,出现BUG(插件已更新).
但是不能总等插件更新,就自己动动手写了一个Native.js版本,由于手上设备不多,没有测试更多的兼容性.在这里希望大家能勇于使用修改提出问题,完善此通知提示!
附件是线控耳机、蓝牙耳机按键监听插件(API >= 26,X86好像不支持忘了)

继续阅读 »

代码

const androidNotifi = {  
  NotifyID: 237,  
  receiver:null,  
  NotifiService:null,  
  serv: null,  
  intent: null,  
  mNotificationBuild: null,  
  mediaBtn: null,  
  channelID : '1',  
  channelName : '音乐通知',  
  notifiImg:{},  
  getImgTemp: function(audioImage) {  
    return new Promise(resolve => {  
      if (this.notifiImg[encodeURIComponent(audioImage)]) {  
        return resolve(plus.io.convertLocalFileSystemURL(this.notifiImg[encodeURIComponent(audioImage)]));  
      }  
      if(!/^http/.test(audioImage)) {  
        this.notifiImg[encodeURIComponent(audioImage)] = audioImage ? audioImage : '_www/static/image/notifimusic.png';  
        return resolve(plus.io.convertLocalFileSystemURL(audioImage ? audioImage : '_www/static/image/notifimusic.png'));  
      }  
      uni.downloadFile({  
        url: audioImage,  
        complete: res => {  
          this.notifiImg[encodeURIComponent(audioImage)] = res.tempFilePath ? res.tempFilePath : '_www/static/image/notifimusic.png';  
          return resolve(plus.io.convertLocalFileSystemURL(res.tempFilePath ? res.tempFilePath : '_www/static/image/notifimusic.png'));  
        }  
      });  
    });  
  },  
  hideNotifi: function(server = false){  
    this.main&&this.receiver&&this.main.unregisterReceiver(this.receiver);   
    server&&this.serv&&this.serv.stopForeground&&this.serv.stopForeground(this.NotifyID, this.mNotificationBuild) ;  
    if (Number(plus.os.version.split('.')[0])>= 8){     
      this.NotifiService&&this.NotifiService.cancel&&this.NotifiService.cancel(this.channelID,this.NotifyID);   
    } else {      
      this.NotifiService&&this.NotifiService.cancel&&this.NotifiService.cancel(this.NotifyID);   

    }   
    this.receiver = null;  
    this.NotifiService = null;  
    this.serv = null;  
    this.intent = null;  
    this.mNotificationBuild = null;  
    // this.NotifiService&&this.NotifiService.cancelAll&&this.NotifiService.cancelAll();  
    this.mediaBtn ? this.mediaBtn.release() : ''; // 线控销毁  
  },  
  showNotifi:async function(param) {  
    if (uni.$_sko.systemInfo.platform != 'android') {   
      return;  
    }  
    try{  
      param = Object.assign({  
        title : '通知标题',  
        subTitle: '',  
        cont : '通知内容',  
        isPlay: true, // 媒体播放按钮  
        legIcon : plus.io.convertLocalFileSystemURL('_www/static/image/notifimusic.png'),  
        Ongoing : false,    // 不可删除  
      },param);  
      this.main = this.main ? this.main : plus.android.runtimeMainActivity();    
      var Context = plus.android.importClass("android.content.Context");    
      var BitmapFactory = plus.android.importClass("android.graphics.BitmapFactory");    
      var NotificationManager = plus.android.importClass("android.app.NotificationManager");  
      var Notification = plus.android.importClass("android.app.Notification");;    
      var Intent = plus.android.importClass("android.content.Intent");    
      var PendingIntent = plus.android.importClass("android.app.PendingIntent");    
      var androidR = plus.android.importClass("android.R");    
      this.NotifiService = this.NotifiService ? this.NotifiService : this.main.getSystemService(Context.NOTIFICATION_SERVICE);    
      this.intent = this.intent ? this.intent : new Intent(this.main, this.main.getClass());    
      var UNI_MEDIA_BROAD = this.main.getPackageName() + '.mediaBtn';  

      //PendingIntent.getActivity的第二个参数需要设置为随机数,否则多个通知时会导致前面的通知被后面的通知替换Extra的数据    
      var pendingIntent = PendingIntent.getActivity(this.main, 0, this.intent, PendingIntent.FLAG_CANCEL_CURRENT);    
      var firstVersionNumber = Number(plus.os.version.split('.')[0]);    
      var mNotification;    
       //判断当前系统版本在8.0及以上    
      if (firstVersionNumber >= 8){  
          if (this.NotifiService.getNotificationChannel() == null){    
              var NotificationChannel = plus.android.importClass('android.app.NotificationChannel');    
              var channel = new NotificationChannel(this.channelID, this.channelName, NotificationManager.IMPORTANCE_HIGH);      
              this.NotifiService.createNotificationChannel(channel);      
              plus.android.autoCollection(NotificationChannel);  
          }    
          param.callBack&&this.main.startForegroundService(this.intent);  
          mNotification = new Notification.Builder(this.main, this.channelID);    
      } else {      
          mNotification = new Notification.Builder(this.main);      
          param.callBack&&this.main.startService(this.intent);  
      }      

      if(param.callBack) {  
        mNotification.addAction(androidR.drawable.ic_media_previous, "prev", PendingIntent.getBroadcast(this.main, 0, new Intent().setAction(UNI_MEDIA_BROAD + '.prev'), PendingIntent.FLAG_CANCEL_CURRENT));  
        mNotification.addAction((param.isPlay ? androidR.drawable.ic_media_pause : androidR.drawable.ic_media_play), "play", PendingIntent.getBroadcast(this.main, 0, new Intent().setAction(UNI_MEDIA_BROAD + '.play'), PendingIntent.FLAG_CANCEL_CURRENT));  
        mNotification.addAction(androidR.drawable.ic_media_next, "next", PendingIntent.getBroadcast(this.main, 0, new Intent().setAction(UNI_MEDIA_BROAD + '.next'), PendingIntent.FLAG_CANCEL_CURRENT));  
        var mMediaStyle = new Notification.MediaStyle();  
        mMediaStyle.setShowActionsInCompactView(0, 1, 2);  
        mNotification.setStyle(mMediaStyle);  
      }  
      mNotification.setOngoing(param.Ongoing);        
      param.legIcon&&mNotification.setLargeIcon(BitmapFactory.decodeFile(await this.getImgTemp(param.legIcon)));  
       mNotification.setSmallIcon(param.callBack?androidR.drawable.stat_sys_headset:androidR.drawable.stat_notify_chat); //设置图标    
       mNotification.setContentTitle(param.title);//设置标题  
       mNotification.setContentText(param.cont); //设置内容    
       param.subTitle&&mNotification.setSubText(param.subTitle);        //子内容暂时去掉    
       // mNotification.setAutoCancel(true);       //设置点击消失    
       //  mNotification.setShowWhen(true);         //显示通知时间    
       mNotification.setTicker("PadInfo");       //弹出通知    
       mNotification.setDefaults(Notification.DEFAULT_ALL);    
       // mNotification.setPriority(Notification.PRIORITY_DEFAULT);   //通知优先级      
       // mNotification.flags=Notification.FLAG_ONLY_ALERT_ONCE;     //发起通知时震动      
       mNotification.setContentIntent(pendingIntent);      

       this.mNotificationBuild = mNotification.build();  
       if (firstVersionNumber>= 8){      
         this.NotifiService.notify(this.channelID, this.NotifyID, this.mNotificationBuild);        
       } else {      
         this.NotifiService.notify(this.NotifyID, this.mNotificationBuild);      
       }   
       if(param.callBack) {  
          this.main&&this.receiver&&this.main.unregisterReceiver(this.receiver);   
          this.receiver = this.receiver ? this.receiver : plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {    
            onReceive: function(context, intent) { //实现onReceiver回调函数    
              param.callBack(intent.getAction().replace(UNI_MEDIA_BROAD + '.',''));    
            }    
          });   
        var IntentFilter = plus.android.importClass('android.content.IntentFilter');    
        var filter = new IntentFilter();    
        filter.addAction(UNI_MEDIA_BROAD + '.prev');  
        filter.addAction(UNI_MEDIA_BROAD + '.play');  
        filter.addAction(UNI_MEDIA_BROAD + '.next');  
        this.main.unregisterReceiver(this.receiver);  
        this.main.registerReceiver(this.receiver, filter); //注册监听    
        this.mediaBtn = uni.requireNativePlugin('uniMediaButton');  // 监听线控  需要配合附件中的插件( Android API >= 26)  
        this.mediaBtn.initMediaButton(res => {  
           console.log(res.message);  
          // 绑定失败/销毁  
          if(res.code == '-1') {  
            this.mediaBtn = null;  
            return;  
          }  
           // res.keyAction == 1 按键松开 res.keyAction == 0 按键按下 播放按钮肯能只有按下  
          if(res.keyCode && res.keyAction == '0') {  
            switch (res.keyCode){  
              case 88:  
                param.callBack(  
                  'prev'  
                );  
                break;  
              case 87:  
                param.callBack(  
                  'next'  
                );  
                break;  
              case 127:  
              case 126:  
                param.callBack(  
                  'play'  
                );  
                break;  
            }  
          }  
        });  
        var serv = plus.android.importClass("android.app.Service");  
        this.serv = this.serv ? this.serv : new serv();  
        this.serv.startForeground(this.NotifyID, this.mNotificationBuild);  
        plus.android.autoCollection(serv);  
        plus.android.autoCollection(IntentFilter);  
       }  
      plus.android.autoCollection(Context);  
      plus.android.autoCollection(BitmapFactory);  
      plus.android.autoCollection(NotificationManager);  
      plus.android.autoCollection(Notification);  
      plus.android.autoCollection(Intent);  
      plus.android.autoCollection(PendingIntent);  
      plus.android.autoCollection(androidR);  
      plus.android.autoCollection(this.receiver);  

    }catch(e){  
      console.log(e);  
    }  
  }  
};

使用方法

// 开启 - 更新标题/播放(暂停)按钮  
show();  
function show(){  
    androidNotifi.showNotifi({  
        title : '通知标题',  
        subTitle: '通知小标题',  
        cont : '通知内容',  
        isPlay: true, // 媒体播放按钮状态(控制播放/暂停按钮样式)  
        legIcon : '', // 封面图片  
        Ongoing : false,    // 是否可删除  
        callBack:function(res){  
            switch (res){  
            case 'play':  
        console.log('play');  
        break;  
        case 'prev':  
        console.log('prev');  
        break;  
        case 'next':  
        console.log('next');  
        break;  
        }  
        }  
    })  
}  
// 关闭  
function hide(){  
    androidNotifi.hideNotifi();  
}  

由于Android播放视频没有通知栏控制.开始一直使用XZH-musicNotification 的插件,但是由于版本更新,出现BUG(插件已更新).
但是不能总等插件更新,就自己动动手写了一个Native.js版本,由于手上设备不多,没有测试更多的兼容性.在这里希望大家能勇于使用修改提出问题,完善此通知提示!
附件是线控耳机、蓝牙耳机按键监听插件(API >= 26,X86好像不支持忘了)

收起阅读 »

银联云闪付小程序开发

云闪付小程序

分享一下我们开发的云闪付小程序PHP后端开发包,unionpay 云闪付小程序开发包

银联云闪付小程序非官方开发包,这可能是第一个支持composer导入的云闪付小程序开发包,小程序后端相关接口与支付相关接口已全部更新完毕。

码云地址:https://gitee.com/leapy/unionpay
Github地址:https://github.com/ileapy/unionpay

vue前端开发包(不建议这样引用)

> npm install upsdk-vue

有需要公众号和小程序开发的可以联系我,我们有团队:

邮箱:cfn@leapy.cn
微信:SH-CFN

继续阅读 »

分享一下我们开发的云闪付小程序PHP后端开发包,unionpay 云闪付小程序开发包

银联云闪付小程序非官方开发包,这可能是第一个支持composer导入的云闪付小程序开发包,小程序后端相关接口与支付相关接口已全部更新完毕。

码云地址:https://gitee.com/leapy/unionpay
Github地址:https://github.com/ileapy/unionpay

vue前端开发包(不建议这样引用)

> npm install upsdk-vue

有需要公众号和小程序开发的可以联系我,我们有团队:

邮箱:cfn@leapy.cn
微信:SH-CFN

收起阅读 »

在uniapp中使用Native.js 使用字节流读写文件工具类封装

/**

  • @description: gpb文件读写工具 采用Native.js
  • @author: Marco
  • @email: wtz_xupt@126.com
    */
    export default class GpbFileUtil {
    /**

    • 单例模式 获取实例
      */
      static getIntance(){
      if(!GpbFileUtil.instance){
      GpbFileUtil.instance = new GpbFileUtil()
      }

      return GpbFileUtil.instance
      }

      /**

    • 构造函数
      */
      constructor(){
      this.init()
      }

      /**

    • 初始化
      */
      init(){
      //导入类需要时间,统一导入提高效率
      this.main = plus.android.runtimeMainActivity()
      this.Environment = plus.android.importClass('android.os.Environment')
      this.File = plus.android.importClass('java.io.File')
      this.FileInputStream = plus.android.importClass("java.io.FileInputStream")
      this.FileOutputStream = plus.android.importClass('java.io.FileOutputStream')
      }

      /**

    • 判断有没有插入sdcard
      */
      checkSDCardExist(){
      if(this.Environment.getExternalStorageState() !== this.Environment.MEDIA_MOUNTED){
      return false
      }
      return true
      }

      /**

    • 获取sdcard根路径
      */
      getSDCardRoot(){
      return this.Environment.getExternalStorageDirectory()
      }

      /**

    • 判断sdcard 文件是否存在
    • @param {Object} filePath
    • @param {Object} fileName
      */
      checkSDCardFileExist(filePath,fileName){
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName
      let file = new this.File(fileFullPath)
      return file.exists()
      }

      /**

    • 删除sdcard中的文件
    • @param {Object} filePath 文件路径
    • @param {Object} fileName 文件名称
      */
      deleteSDCardFile(filePath,fileName){
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName

       let file = new this.File(fileFullPath)  
       if(file.exists()){  
           file.delete()  
       }  

      }

      /**

    • 读取sdcard中文件内容
    • @param {Object} filePath 文件路径
    • @param {Object} fileName 文件名称
      */
      readFileFromSDCard(filePath,fileName){
      if(!this.checkSDCardFileExist(filePath,fileName)){
      plus.nativeUI.toast(fileName + '文件不存在!')
      return;
      }
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName

      let file = new this.File(fileFullPath)

      let fis = new this.FileInputStream(file)

      let bytes = new Uint8Array(fis.available())
      try{
      let index = 0
      let byte
      while((byte = fis.read()) !== -1){
      bytes[index] = byte
      ++index
      }

      }catch(e){
      //TODO handle the exception
      console.log('读取sd文件' + fileName + '失败:' + e.message)
      }finally{
      try{
      if(fis !== null){
      fis.close()
      }
      }catch(ex){
      //TODO handle the exception
      console.log('关闭文件输入流失败:' + ex.message)
      }
      }

      return bytes
      }

      /**

    • 向sdcard中写文件
    • @param {Object} filePath 文件路径
    • @param {Object} fileName 文件名称
    • @param {Object} fileContentBytes 文件内容字节数组
      */
      writeFileToSDCard(filePath,fileName,fileContentBytes){
      this.deleteSDCardFile()
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName
      let file = new this.File(fileFullPath)
      let fos = new this.FileOutputStream(file)
      try{
      let index = 0
      while(index < fileContentBytes.length){
      fos.write(fileContentBytes[index])
      ++index
      }
      //fos.write(fileContentBytes) //java byte数组在nativejs中写入不了,只能循环一个一个字节写入
      fos.flush()
      }catch(e){
      //TODO handle the exception
      console.log('写入sd文件' + fileName + '失败:' + e.message)
      }finally{
      try{
      if(fos !== null){
      fos.close()
      }
      }catch(ex){
      //TODO handle the exception
      console.log('关闭文件输出流失败:' + ex.message)
      }
      }
      }

    }

在测试过程中nativejs 调用java FileInputStream read(byte []) 和write(byte []) 读取和写入不了。尝试循环后每次读取或写入一个字节成功了。

继续阅读 »

/**

  • @description: gpb文件读写工具 采用Native.js
  • @author: Marco
  • @email: wtz_xupt@126.com
    */
    export default class GpbFileUtil {
    /**

    • 单例模式 获取实例
      */
      static getIntance(){
      if(!GpbFileUtil.instance){
      GpbFileUtil.instance = new GpbFileUtil()
      }

      return GpbFileUtil.instance
      }

      /**

    • 构造函数
      */
      constructor(){
      this.init()
      }

      /**

    • 初始化
      */
      init(){
      //导入类需要时间,统一导入提高效率
      this.main = plus.android.runtimeMainActivity()
      this.Environment = plus.android.importClass('android.os.Environment')
      this.File = plus.android.importClass('java.io.File')
      this.FileInputStream = plus.android.importClass("java.io.FileInputStream")
      this.FileOutputStream = plus.android.importClass('java.io.FileOutputStream')
      }

      /**

    • 判断有没有插入sdcard
      */
      checkSDCardExist(){
      if(this.Environment.getExternalStorageState() !== this.Environment.MEDIA_MOUNTED){
      return false
      }
      return true
      }

      /**

    • 获取sdcard根路径
      */
      getSDCardRoot(){
      return this.Environment.getExternalStorageDirectory()
      }

      /**

    • 判断sdcard 文件是否存在
    • @param {Object} filePath
    • @param {Object} fileName
      */
      checkSDCardFileExist(filePath,fileName){
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName
      let file = new this.File(fileFullPath)
      return file.exists()
      }

      /**

    • 删除sdcard中的文件
    • @param {Object} filePath 文件路径
    • @param {Object} fileName 文件名称
      */
      deleteSDCardFile(filePath,fileName){
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName

       let file = new this.File(fileFullPath)  
       if(file.exists()){  
           file.delete()  
       }  

      }

      /**

    • 读取sdcard中文件内容
    • @param {Object} filePath 文件路径
    • @param {Object} fileName 文件名称
      */
      readFileFromSDCard(filePath,fileName){
      if(!this.checkSDCardFileExist(filePath,fileName)){
      plus.nativeUI.toast(fileName + '文件不存在!')
      return;
      }
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName

      let file = new this.File(fileFullPath)

      let fis = new this.FileInputStream(file)

      let bytes = new Uint8Array(fis.available())
      try{
      let index = 0
      let byte
      while((byte = fis.read()) !== -1){
      bytes[index] = byte
      ++index
      }

      }catch(e){
      //TODO handle the exception
      console.log('读取sd文件' + fileName + '失败:' + e.message)
      }finally{
      try{
      if(fis !== null){
      fis.close()
      }
      }catch(ex){
      //TODO handle the exception
      console.log('关闭文件输入流失败:' + ex.message)
      }
      }

      return bytes
      }

      /**

    • 向sdcard中写文件
    • @param {Object} filePath 文件路径
    • @param {Object} fileName 文件名称
    • @param {Object} fileContentBytes 文件内容字节数组
      */
      writeFileToSDCard(filePath,fileName,fileContentBytes){
      this.deleteSDCardFile()
      let fileFullPath = this.getSDCardRoot() + this.File.separator + filePath + this.File.separator + fileName
      let file = new this.File(fileFullPath)
      let fos = new this.FileOutputStream(file)
      try{
      let index = 0
      while(index < fileContentBytes.length){
      fos.write(fileContentBytes[index])
      ++index
      }
      //fos.write(fileContentBytes) //java byte数组在nativejs中写入不了,只能循环一个一个字节写入
      fos.flush()
      }catch(e){
      //TODO handle the exception
      console.log('写入sd文件' + fileName + '失败:' + e.message)
      }finally{
      try{
      if(fos !== null){
      fos.close()
      }
      }catch(ex){
      //TODO handle the exception
      console.log('关闭文件输出流失败:' + ex.message)
      }
      }
      }

    }

在测试过程中nativejs 调用java FileInputStream read(byte []) 和write(byte []) 读取和写入不了。尝试循环后每次读取或写入一个字节成功了。

收起阅读 »