mirror of
https://github.com/openstf/stf
synced 2025-10-06 03:50:04 +02:00
Implement device screen.
This commit is contained in:
parent
a2dd5ad0c7
commit
d17e86bc57
9 changed files with 317 additions and 1 deletions
|
@ -79,6 +79,7 @@ module.exports = function(options) {
|
|||
var whitelist = {
|
||||
'devices/index': true
|
||||
, 'devices/control': true
|
||||
, 'devices/screen': true
|
||||
}
|
||||
|
||||
if (whitelist.hasOwnProperty(req.params[0])) {
|
||||
|
|
|
@ -355,27 +355,51 @@ module.exports = function(options) {
|
|||
})
|
||||
.on(wire.TouchDownMessage, function(channel, message) {
|
||||
services.input.touchDownAsync(message.x, message.y)
|
||||
.catch(function(err) {
|
||||
log.error('touchDown failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.TouchMoveMessage, function(channel, message) {
|
||||
services.input.touchMoveAsync(message.x, message.y)
|
||||
.catch(function(err) {
|
||||
log.error('touchMove failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.TouchUpMessage, function(channel, message) {
|
||||
services.input.touchUpAsync(message.x, message.y)
|
||||
.catch(function(err) {
|
||||
log.error('touchUp failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.TapMessage, function(channel, message) {
|
||||
services.input.tapAsync(message.x, message.y)
|
||||
.catch(function(err) {
|
||||
log.error('tap failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.TypeMessage, function(channel, message) {
|
||||
services.monkey.typeAsync(message.text)
|
||||
.catch(function(err) {
|
||||
log.error('type failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.KeyDownMessage, function(channel, message) {
|
||||
services.monkey.keyDownAsync(message.key)
|
||||
.catch(function(err) {
|
||||
log.error('keyDown failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.KeyUpMessage, function(channel, message) {
|
||||
services.monkey.keyUpAsync(message.key)
|
||||
.catch(function(err) {
|
||||
log.error('keyUp failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.KeyPressMessage, function(channel, message) {
|
||||
services.monkey.pressAsync(message.key)
|
||||
.catch(function(err) {
|
||||
log.error('keyPress failed', err.stack)
|
||||
})
|
||||
})
|
||||
.on(wire.ShellCommandMessage, function(channel, message) {
|
||||
log.info('Running shell command "%s"', message.command.join(' '))
|
||||
|
|
|
@ -3,10 +3,11 @@ define(['./_module'], function(app) {
|
|||
$scope.device = null
|
||||
$scope.control = null
|
||||
|
||||
deviceService.get($routeParams.serial)
|
||||
$scope.promiseOfDevice = deviceService.get($routeParams.serial)
|
||||
.then(function(device) {
|
||||
$scope.device = device
|
||||
$scope.control = controlService.forChannel(device.channel)
|
||||
return device
|
||||
})
|
||||
}
|
||||
|
||||
|
|
140
res/app/scripts/controllers/DeviceScreenCtrl.js
Normal file
140
res/app/scripts/controllers/DeviceScreenCtrl.js
Normal file
|
@ -0,0 +1,140 @@
|
|||
define(['./_module'], function(app) {
|
||||
function DeviceScreenCtrl($scope, scalingService) {
|
||||
$scope.ready = false
|
||||
$scope.displayError = false
|
||||
$scope.scalingService = scalingService
|
||||
|
||||
$scope.promiseOfDevice.then(function() {
|
||||
$scope.ready = true
|
||||
})
|
||||
}
|
||||
|
||||
function DeviceScreenDirective($document, scalingService) {
|
||||
return {
|
||||
restrict: 'E'
|
||||
, templateUrl: 'partials/devices/screen'
|
||||
, link: function($scope, element, attrs) {
|
||||
$scope.promiseOfDevice.then(function(device) {
|
||||
var loader = new Image()
|
||||
, canvas = element.find('canvas')[0]
|
||||
, g = canvas.getContext('2d')
|
||||
, displayWidth = 0
|
||||
, displayHeight = 0
|
||||
, scaler = scalingService.coordinator(
|
||||
device.display.width
|
||||
, device.display.height
|
||||
)
|
||||
|
||||
function updateDisplaySize() {
|
||||
displayWidth = element[0].offsetWidth
|
||||
displayHeight = element[0].offsetHeight
|
||||
|
||||
// Developer error, let's try to reduce debug time
|
||||
if (!displayWidth || !displayHeight) {
|
||||
throw new Error(
|
||||
'Unable to update display size; container must have dimensions'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function loadScreen() {
|
||||
loader.src = device.display.url +
|
||||
'?width=' + displayWidth +
|
||||
'&height=' + displayHeight +
|
||||
'&time=' + Date.now()
|
||||
}
|
||||
|
||||
loader.onload = function() {
|
||||
var size = scaler.projectedSize(displayWidth, displayHeight)
|
||||
|
||||
// Make sure we're rendering pixels 1 to 1
|
||||
canvas.width = this.width
|
||||
canvas.height = this.height
|
||||
|
||||
// Perhaps we have a massive screen but not enough pixels. Let's
|
||||
// scale up
|
||||
canvas.style.width = size.width + 'px'
|
||||
canvas.style.height = size.height + 'px'
|
||||
|
||||
// Draw the image
|
||||
g.drawImage(this, 0, 0)
|
||||
|
||||
// Reset error, if any
|
||||
if ($scope.displayError) {
|
||||
$scope.$apply(function() {
|
||||
$scope.displayError = false
|
||||
})
|
||||
}
|
||||
|
||||
// Next please
|
||||
loadScreen()
|
||||
}
|
||||
|
||||
loader.onerror = function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.displayError = true
|
||||
})
|
||||
}
|
||||
|
||||
function sendTouch(type, e) {
|
||||
var scaled = scaler.coords(
|
||||
displayWidth
|
||||
, displayHeight
|
||||
, e.offsetX
|
||||
, e.offsetY
|
||||
)
|
||||
|
||||
$scope.control[type](
|
||||
scaled.xP * device.display.width
|
||||
, scaled.yP * device.display.height
|
||||
)
|
||||
}
|
||||
|
||||
function downListener(e) {
|
||||
e.preventDefault()
|
||||
sendTouch('touchDown', e)
|
||||
element.bind('mousemove', moveListener)
|
||||
$document.bind('mouseup', upListener)
|
||||
$document.bind('mouseleave', upListener)
|
||||
}
|
||||
|
||||
function moveListener(e) {
|
||||
sendTouch('touchMove', e)
|
||||
}
|
||||
|
||||
function upListener(e) {
|
||||
sendTouch('touchUp', e)
|
||||
stop()
|
||||
}
|
||||
|
||||
function stop() {
|
||||
element.unbind('mousemove', moveListener)
|
||||
$document.unbind('mouseup', upListener)
|
||||
$document.unbind('mouseleave', upListener)
|
||||
}
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
loader.onload = loader.onerror = null
|
||||
$document.unbind('mouseup', upListener)
|
||||
$document.unbind('mouseleave', upListener)
|
||||
})
|
||||
|
||||
element.bind('mousedown', downListener)
|
||||
updateDisplaySize()
|
||||
loadScreen()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.controller('DeviceScreenCtrl'
|
||||
, [ '$scope'
|
||||
, 'ScalingService'
|
||||
, DeviceScreenCtrl
|
||||
])
|
||||
.directive('deviceScreen'
|
||||
, [ '$document'
|
||||
, 'ScalingService'
|
||||
, DeviceScreenDirective
|
||||
])
|
||||
})
|
|
@ -1,6 +1,7 @@
|
|||
define([
|
||||
'./DeviceListCtrl'
|
||||
, './DeviceControlCtrl'
|
||||
, './DeviceScreenCtrl'
|
||||
]
|
||||
, function() {
|
||||
}
|
||||
|
|
128
res/app/scripts/services/ScalingService.js
Normal file
128
res/app/scripts/services/ScalingService.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
define(['./_module'], function(app) {
|
||||
function ScalingServiceFactory() {
|
||||
var scalingService = {
|
||||
}
|
||||
|
||||
scalingService.coordinator = function(realWidth, realHeight) {
|
||||
var realRatio = realWidth / realHeight
|
||||
|
||||
return {
|
||||
coords: function(width, height, x, y) {
|
||||
var ratio = width / height
|
||||
, scaledValue
|
||||
|
||||
if (realRatio > ratio) {
|
||||
// covers the area horizontally
|
||||
scaledValue = width / realRatio;
|
||||
|
||||
// adjust y to start from the scaled top edge
|
||||
y -= (height - scaledValue) / 2
|
||||
|
||||
// not touching the screen, but we want to trigger certain events
|
||||
// (like touchup) anyway, so let's do it on the edges.
|
||||
if (y < 0) {
|
||||
y = 0
|
||||
}
|
||||
else if (y > scaledValue) {
|
||||
y = scaledValue
|
||||
}
|
||||
|
||||
// make sure x is within bounds too
|
||||
if (x < 0) {
|
||||
x = 0
|
||||
}
|
||||
else if (x > width) {
|
||||
x = width
|
||||
}
|
||||
|
||||
height = scaledValue
|
||||
}
|
||||
else {
|
||||
// covers the area vertically
|
||||
scaledValue = height * realRatio
|
||||
|
||||
// adjust x to start from the scaled left edge
|
||||
x -= (width - scaledValue) / 2
|
||||
|
||||
// not touching the screen, but we want to trigger certain events
|
||||
// (like touchup) anyway, so let's do it on the edges.
|
||||
if (x < 0) {
|
||||
x = 0
|
||||
}
|
||||
else if (x > scaledValue) {
|
||||
x = scaledValue
|
||||
}
|
||||
|
||||
// make sure y is within bounds too
|
||||
if (y < 0) {
|
||||
y = 0
|
||||
}
|
||||
else if (y > height) {
|
||||
y = height
|
||||
}
|
||||
|
||||
width = scaledValue
|
||||
}
|
||||
|
||||
return {
|
||||
xP: x / width
|
||||
, yP: y / height
|
||||
}
|
||||
}
|
||||
, size: function(width, height) {
|
||||
var ratio = width / height
|
||||
|
||||
if (realRatio > ratio) {
|
||||
// covers the area horizontally
|
||||
|
||||
if (width >= realWidth) {
|
||||
// don't go over max size
|
||||
width = realWidth
|
||||
height = realHeight
|
||||
}
|
||||
else {
|
||||
height = Math.floor(width / realRatio)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// covers the area vertically
|
||||
|
||||
if (height >= realHeight) {
|
||||
// don't go over max size
|
||||
height = realHeight
|
||||
width = realWidth
|
||||
}
|
||||
else {
|
||||
width = Math.floor(height * realRatio)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: width
|
||||
, height: height
|
||||
}
|
||||
}
|
||||
, projectedSize: function(width, height) {
|
||||
var ratio = width / height
|
||||
|
||||
if (realRatio > ratio) {
|
||||
// covers the area horizontally
|
||||
height = Math.floor(width / realRatio)
|
||||
}
|
||||
else {
|
||||
width = Math.floor(height * realRatio)
|
||||
}
|
||||
|
||||
return {
|
||||
width: width
|
||||
, height: height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scalingService
|
||||
}
|
||||
|
||||
app.factory('ScalingService', [ScalingServiceFactory])
|
||||
})
|
|
@ -4,6 +4,7 @@ define([
|
|||
, './GroupService'
|
||||
, './UserService'
|
||||
, './ControlService'
|
||||
, './ScalingService'
|
||||
]
|
||||
, function() {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
h1 {{ device.serial }}
|
||||
|
||||
style.
|
||||
device-screen {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
device-screen canvas {
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none; /* MUST HAVE */
|
||||
}
|
||||
|
||||
div(ng-controller='DeviceScreenCtrl')
|
||||
device-screen(style='width: 400px; height: 600px; background: gray')
|
||||
|
||||
button(ng-click='control.menu()') Menu
|
||||
button(ng-click='control.home()') Home
|
||||
button(ng-click='control.back()') Back
|
||||
|
|
2
res/app/views/partials/devices/screen.jade
Normal file
2
res/app/views/partials/devices/screen.jade
Normal file
|
@ -0,0 +1,2 @@
|
|||
canvas(ng-show='ready')
|
||||
div(ng-if='displayError') Screen error
|
Loading…
Add table
Add a link
Reference in a new issue