stf二次开发之添加前端扫码并远程打开浏览器访问网页

需求

  1. 在前端网页上传二维码图片,js解码后解析出网页链接
  2. 控制远程手机启动浏览器访问该网页链接

分析

  1. js解码库确定使用reqrcodejs,使用简单
  2. 打开浏览器可以参考stf源码中的browseropen

前端部分

stf二次开发之添加前端扫码并远程打开浏览器访问网页_第1张图片
如图所示,在手机控制工具栏一侧增加扫码标签页。点击呈现扫码页。

  1. 选择二维码文件上传
  2. 点击解析二维码并打开手机浏览器访问网页链接 按钮,就可解析出二维码包含的链接并在手机浏览器打开。

stf 的前端是 用pug(原jade)模板,angularjs 编写, webpack打包构建的。

angularjs的语法可以从相关网站获取。

说下 新增文件 先:
res/app下是前端部分。
在res/app/control-panes下新建:

qrscan目录
	1  index.js      //载入依赖,加载页面pug模板,设置逻辑控制器
	2  qrscan.pug //页面
	3  qrscan.css
	4  qrscan-controller.js //控制器
	5  qrscan-spec.js       //

当在control-panes添加一个新的模块时, 要添加到control-panes依赖中,否则不能被识别到。

//res/app/control-panes/index.js
module.exports = angular.module('control-panes', [
......
require('./qrscan').name
])

UI 图上新增的扫码标签页,在control-panes-controller里添加

//res/app/control-panes/control-pane-controller.js
module.exports =
  function ControlPanesController(......) {
	var sharedTabs = [
	......
	//添加扫码标签页
      {
        title: gettext('Scan'),
        templateUrl: 'control-panes/qrscan/qrscan.pug', //指定渲染的页面
        filters: ['native', 'web']
      }
]
}

在qrscan/index.js里扫码模块 的 载入依赖,加载页面pug模板,设置逻辑控制器。

//res/app/control-pane/qrscan/index.js
require('./qrscan.css')
require('ng-file-upload') //上传文件所需模块

module.exports = angular.module('stf.qrscan', [
  'angularFileUpload', //上传文件所需依赖
  require('stf/settings').name,
  require('stf/storage').name,
  require('stf/upload').name,
  require('stf/common-ui').name,
  require('stf/image-onload').name,
  require('gettext').name
])
  .run(['$templateCache', function($templateCache) {
    $templateCache.put(
      'control-panes/qrscan/qrscan.pug'
      , require('./qrscan.pug') //渲染页面设置
    )
  }])
  .controller('QrscanCtrl', require('./qrscan-controller')) //QrscanCtrl 为 qrscan-controller的别名。 设置控制器

  • qrscan.pug 代码
.fill-height(ng-controller="QrscanCtrl").stf-qrscan

  .widget-content.padded
      .drop-area(ng-file-drop='dropFile($files)', ng-file-drag-over-class='dragover').file-input.btn-file
        input(type='file', ng-file-select='uploadQRcode($files)',  ng-model="data.file")#img-input
        i.fa.fa-2x.fa-download.drop-area-icon
        .drop-area-text(translate) Drop Qrcode to upload

  div.picture
      img(ng-src='{{ imgSrc }}').qrscan-image.drop-area

  div(id="tribtn").widget-content.padded.singleline
    button.btn.btn-sm.btn-primary-outline.btn_menu(ng-click='saveToDcim()',
      title='{{"saveToDcim"|translate}}')
      i.fa.fa-save
      span(translate) saveToDcim
    button.btn.btn-sm.btn-primary-outline.btn_menu(onclick='openBrowser()'
      title='{{"scan_qrcode"|translate}}')
      i.fa.fa-camera
      span(translate) scan_qrcode
    button.btn.btn-sm.btn-primary-outline.btn_menu(onclick='pasteToClipboard()'
      title='{{"pasteToClipboard"|translate}}')
      i.fa.fa-copy
      span(translate) pasteToClipboard

    .clearfix

比较简单,分为四个部分:

  1. ng-controller =“QrscanCtrl” //绑定qrscan-controller
  2. 选择图片文件上传
  3. 预览图片
  4. button区

可能一开始对pug模板这种类型,写着不熟悉,可以先写好html,再根据Pug的语法再写一遍。

这里提一个 命令行编译pug,编译成html

  1. 安装 npm install -g pug-cli
  2. 执行 pug index.pug ,输出 "rendered index.html"这就表示编译成功,我们去文件夹中看,就会发现多了一个index.html文件
  3. 实时编译 pug -P -w index.pug , 实时编译就是当我们修改index.pug时,index.html也会被改变.

说下这个页面的交互事件:

  • input(type=‘file’, ng-file-select=‘uploadQRcode($files)’

input选择文件后,触发ng-file-select 标注的 uploadQRcode方法(调用qrscan-controller.js里的($scope.uploadQRcode方法)

$files是文件形参.

  • img(ng-src=’{{ imgSrc }}’)
    接受qrscan-controller.js的$scope.imgSrc变量, 使用imgSrc的值渲染预览图片

  • button.btn.btn-sm.btn-primary-outline.btn_menu(ng-click=‘saveToDcim()’,
    ng-click 是当该button点击时,调用qrscan-controller.js (angularjs)的$scope.saveToDcim函数。

  • οnclick=‘openBrowser()’ ,οnclick=‘pasteToClipboard()’
    而这两个button , 调用的是纯Js函数。

上传文件

qrscan-controller.js代码

var Promise = require('bluebird')
Promise.longStackTraces()

module.exports = function QrscanCtrl(
  $scope
  , $filter
  , StorageService
  , $http
  , $upload
) {

  $scope.mReader = new FileReader()//处理二维码文件对象
  $scope.mFiles //保存input标签传入的文件对象

$scope.uploadQRcode = function($file){
	if ($files.length) {
      $scope.mFiles = $files
      /**
       * 处理文件,预览二维码图片
       */
      $scope.mReader.readAsDataURL($files[0])//传入File对象,Blob
      $scope.mReader.onloadend = function(ev) {
        $scope.$apply(function() {
          // $scope.thumb = ev.target.result;
          $scope.imgSrc = ev.target.result//接收base64
        })
      }
      return true
    }
}
$scope.saveToDcim = function(){...}
$scope.openBrowser = function(url){...}
$scope.pasteToClipboard = function(text){...}

当用户拖动或选择文件上传时,html的input标签的ng-file-select 标注的 uploadQRcode方法被触发,也就是$scope.uploadQRcode函数。

上述函数先判断参数$files数组长度,通过FileReader (scope.mReader)读取文件数据,然后在onLoadend函数里接受base64赋值给imgSrc, 直接反映在qrscan.pug里的img标签,这样完成对上传图片的预览。

还有一点是保存$files 给 $scope.mFIles, 供后面解码用。

解码

index.pug是stf首页主页面 res/app/views/index.pug

script(src='static/app/views/js/reqrcode.js') #引入reqrcodejs
script.
function scanQrcode(files) {
        //根据文件生成url,供reqrcodejs库的decode方法解析
        var getObjectURL = function(file) {
          var url = null;
          if (window.createObjectURL != undefined) { // basic
            url = window.createObjectURL(file);
          }
          else if (window.URL != undefined) { // mozilla(firefox)
            try {
              url = window.URL.createObjectURL(file);
            } catch (e) {
            }
          }
          else if (window.webkitURL != undefined) { // webkit or chrome
            url = window.webkitURL.createObjectURL(file);
          }
          return url;
        }

        if (files != null && files.length > 0) {
          qrcode.decode(getObjectURL(files[0]));//解析二维码
          return qrcode;
        }
        else {
          alert("请先上传二维码图片文件!")
          return null;
        }
      }

将reqrcode.js源码文件放在res/app/views/js/下,然后script(src)引入(代码第一行).然后如注释所示,使用qrcode api解析二维码,封装成scanQrcode函数,供openBrowser函数调用。

//res/app/views/index.pug
function openBrowser() {
        var appElement = document.querySelector('[ng-controller=QrscanCtrl]');
        var $scope = angular.element(appElement).scope();
        var reqrcode = scanQrcode($scope.mFiles)
        if (reqrcode) {
          reqrcode.callback = function(imgMsg) {
            $scope.openBrowser(imgMsg);
          }
        }
      }

虽然在webstorm中, qrscan.pug里的 openBrowser 事件不能导航到index.pug里,但是不影响运行。

这个逻辑是:
1.qrscan.pug里的openBrowser点击事件 调用 index.pug里的openBrowser js函数。

2.通过document.querySelector和angular.element分别获取到angular controller和scope,拿到$scope.mFiles二维码文件传给scanQrcode函数解码

3.解码后再通过scope调用到qrscan-controller.js里的$scope.openBrowser方法进行后续处理。

综上,完成html调js对二维码解码,然后再调到angular controller里的方法并传参。后面就是向后台发起请求。一系列过程。

写完前端 , 执行 gulp clean , gulp build ,这样 webpack构建,不然修改是不会生效的。
如果提示 gulp not found , 执行 npm install gulp -g 安装gulp.
然后执行stf local查看效果。

接着说下解码之后,拿到链接信息,如何打开手机浏览器访问,也就是$scope.openBrowser所做的事情。

$scope.openBrowser = function(url) {
    if (url.indexOf('http') != -1) {
      alert('网页链接解析成功,正在跳转浏览器...')
      return $scope.control.openBrowser(url, null)
    }
    else {
      alert('该二维码不是正确的链接,二维码文本为' + imgMsg)
    }
    }

关于scope.control 通过IDE查看 能发现是 继承的 control-panes-controller的scope.control. (angularjs 子scope能继承父scope)
在control-panes-controller89行,scope.control = ControlService.create(device, device.channel)
所以$scope.control.openBrowser,也就是 ControlService的openBrowser函数。
打开浏览器功能在控制面板的导航里有实现,所以在做这部分功能就可以借鉴导航 navigation-controller这部分。

后台

进入 control-service.js ,有这样代码

//res/app/components/stf/control/control-service.js
this.openBrowser = function(url, browser) {
      return sendTwoWay('browser.open', {
        url: url
      , browser: browser ? browser.id : null
      })
    }

继续追踪sendTwoWay,其实就是socket发送出去。
那发送出去的信息,哪里接收的呢??
我们发现 browser.open 这样的标志,全局搜它,发现在lib/units/websocket/index.js里有处理.

//lib/units/websocket/index.js
.on('browser.open', function(channel, responseChannel, data) {
          joinChannel(responseChannel)
          push.send([
            channel
          , wireutil.transaction(
              responseChannel
            , new wire.BrowserOpenMessage(data)
            )
          ])
        })

这里又再次封装protobuf,将BrowserOpenMessage 结构体发出去。
同样,全局搜 BrowserOpenMessage。

找到 lib/units/device/plugins/browser.js

//lib/units/device/plugins/browser.js
router.on(wire.BrowserOpenMessage, function(channel, message) {
      message.url = ensureHttpProtocol(message.url)
      ......

      var reply = wireutil.reply(options.serial)
      adb.startActivity(options.serial, {
          action: 'android.intent.action.VIEW'
        , component: message.browser
        , data: message.url
        })
        .then(function() {
          push.send([
            channel
          , reply.okay()
          ])
        })
        .catch(function(err) {
          if (message.browser) {
            log.error(
              'Failed to open "%s" in "%s"'
            , message.url
            , message.browser
            , err.stack
            )
          }
          else {
            log.error('Failed to open "%s"', message.url, err.stack)
          }
          push.send([
            channel
          , reply.fail()
          ])
        })
    })

router接收到BrowserOpenMessage, 解析出Message里的browser和url数据,通过adbkit startActivity传入action , browsername, url打开浏览器访问链接。

总结

文件脉络为 contro-pane/index.js - control-pane-controller.js - qrscan/index.js - qrscan.pug - index.pug - qrscan-controller.js - control-service.js - websocket/index.js - plugins/browser.js

过程走完了。 完成上传二维码-预览-解码-打开浏览器。

最后提下

语言国际化

语言脚本路径: res/common/lang/translations/stf.zh_CN.json

在里面直接元素即可。 比如 “scan”:‘扫码’,“drop files to upload”:“拖动文件以上传”,“openbrowser”:“打开浏览器”

修改后,一定要 gulp build 才会生效。

你可能感兴趣的:(STF)