Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member104694
Participant
The release of iOS 11 (on September 19, 2017) has bought some good news for anyone wanting to incorporate barcode scanning into their SAPUI5 applications - support has been added to for the media capture API. This means there is now the ability to access a device camera (and microphone) directly from Safari (and all other major browsers except Opera mini - see http://caniuse.com/#feat=stream), opening up the possibility to use barcode scanning in applications without the need for a native container (i.e. Cordova).

In this blog I will demonstrate how a device camera can be accessed within a SAPUI5 application to scan a barcode and populate the value of the barcode into an input field. This will be achieved in a plain SAPUI5 web application which can be accessed via any device that has a camera and a supported browser (we will implement a fallback solution when the application is accessed via a desktop or device with no camera). We will make use of an external JavaScript library called QuaggaJS which will handle the detection and decoding of barcodes.

What you will need

  • Some basic SAPUI5 knowledge

  • An IDE or text editor

  • The latest release of QuaggaJS library

  • Somewhere to publish your application

  • A mobile device with a camera


It is assumed that you already have (or know how to create) a UI5 project with a component, manifest, view and controller...if not, then suggest you create one via WebIDE using the "SAPUI5 Application" template.

Once you have your project, create a folder "libs" and add the QuaggaJS library  (quagga.min.js) under it.



Next, specify the resource in your manifest under sap.ui5 > resources > js (you could chose to load the library using an alternate method if preferred)
"resources": {
"css": [
{
"uri": "css/style.css"
}
],
"js": [
{
"uri": "libs/quagga.min.js"
}
]
}

Add a property to the device model to track whether the device accessing our application has video capability. To check this we use method MediaDevices.getUserMedia. Calling this method will prompt the user to allow access to their camera (if the device has one)...note that it is possible to leave this check to a later time (like when the user attempts to click the scan button which is the actual point the application would like to access the camera), however we are doing it here so that the model property can be used in our view to control the visibility of the scan button.
createDeviceModel: function() {
var oModel = new JSONModel(Device);
oModel.setDefaultBindingMode("OneWay");

// Disable the scan barcode button by default
oModel.setProperty("/barcodeScanEnabled",false);
if(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia){
navigator.mediaDevices.getUserMedia({video:true}).then(function(stream){
// device supports video, which means will enable the scan button
oModel.setProperty("/barcodeScanEnabled",true);
}).catch(function(err){
// not supported, barcodeScanEnabled already default to false
});
}

return oModel;
}

Add the following controls to the view: a label, an input control to store the barcode value and a button to trigger the scanning (visible if the device is capable).
<Label text="Barcode value" />
<Input id="scannedValue" placeholder="{= ${device>/barcodeScanEnabled} ? 'Use scan button to enter barcode' : 'Enter barcode manually' }" editable="{= !${device>/barcodeScanEnabled} }" />
<Button icon="sap-icon://bar-code" text="Scan" tooltip="Scan barcode" visible="{device>/barcodeScanEnabled}" press="onScanForValue">
<layoutData>
<l:GridData span="L2 M2" />
</layoutData>
</Button>

Add the two methods below to the controller. The first is the event handler for our scan button press  which will create and open a dialog - the content of the dialog is a single HTML div that is used to display the video feed from the camera. We have attached a handler for the afterOpen event which will call method _initQuagga (our second controller method) to initialise Quagga and then start the video stream. For configuration options see https://serratus.github.io/quaggaJS/#configobject.
onScanForValue: function(oEvent){
if(!this._oScanDialog){
this._oScanDialog = new sap.m.Dialog({
title : "Scan barcode",
contentWidth : "640px",
contentHeight : "480px",
horizontalScrolling : false,
verticalScrolling : false,
stretchOnPhone : true,
content : [new sap.ui.core.HTML({
id : this.createId("scanContainer"),
content : "<div />"
})],
endButton : new sap.m.Button({
text : "Cancel",
press : function(oEvent){
this._oScanDialog.close();
}.bind(this)
}),
afterOpen : function(){
this._initQuagga(this.getView().byId("scanContainer").getDomRef()).done(function(){
// Initialisation done, start Quagga
Quagga.start();
}).fail(function(oError){
// Failed to initialise, show message and close dialog...this should not happen as we have
// already checked for camera device ni /model/models.js and hidden the scan button if none detected
MessageBox.error(oError.message.length ? oError.message : ("Failed to initialise Quagga with reason code " + oError.name),{
onClose: function(){
this._oScanDialog.close();
}.bind(this)
});
}.bind(this));
}.bind(this),
afterClose : function(){
// Dialog closed, stop Quagga
Quagga.stop();
}
});

this.getView().addDependent(this._oScanDialog);
}

this._oScanDialog.open();
},

_initQuagga: function(oTarget){
var oDeferred = jQuery.Deferred();

// Initialise Quagga plugin - see https://serratus.github.io/quaggaJS/#configobject for details
Quagga.init({
inputStream: {
type : "LiveStream",
target : oTarget,
constraints : {
width : {min: 640},
height : {min: 480},
facingMode : "environment"
}
},
locator: {
patchSize : "medium",
halfSample : true
},
numOfWorkers : 2,
frequency : 10,
decoder : {
readers : [{
format : "code_128_reader",
config : {}
}]
},
locate : true
}, function(error) {
if (error) {
oDeferred.reject(error);
} else {
oDeferred.resolve();
}
});

if(!this._oQuaggaEventHandlersAttached){
// Attach event handlers...

Quagga.onProcessed(function(result) {
var drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;

if (result) {
// The following will attempt to draw boxes around detected barcodes
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2});
});
}

if (result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
}

if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
}
}
}.bind(this));

Quagga.onDetected(function(result) {
// Barcode has been detected, value will be in result.codeResult.code. If requierd, validations can be done
// on result.codeResult.code to ensure the correct format/type of barcode value has been picked up

// Set barcode value in input field
this.getView().byId("scannedValue").setValue(result.codeResult.code);

// Close dialog
this._oScanDialog.close();
}.bind(this));

// Set flag so that event handlers are only attached once...
this._oQuaggaEventHandlersAttached = true;
}

return oDeferred.promise();
}

Final step to be able to test your application is to publish it to a location that is accessible by the device you are testing with - this could be a SAP gateway, SCP or any other web server.

A working example can be found here and the full code can be accessed here.

If you need a sample barcode to scan you can use this one or generate your own here.

Note that the code provided is configured to detect barcodes with symbology of Code 128, alternate formats can be specified in the configuration object passed to Quagga.init.

Hopefully you have found parts of this blog useful...feel free to leave any feedback or questions below.
19 Comments
Labels in this area