Skip to Content

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.

To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

    1. Ian MacGregor Post author

      Thanks Mike. It’s already supported in Android if using Android browser, Chrome, Opera (non mini version) or Firefox…other less popular browsers seem to have some support but may require tweaking 🙂

      (0) 
  1. vinayaka first

    Hi Lan,

    I tested in Web IDE ,but here i am not able to see Barcode scanner via camera thru mobile device(andriod).

    I got below output ,could you please tell me how to scan barcode via camera ( Andriod device ).

     

    (0) 
  2. blount international

     

    Hey Lan,
    I am able to scan bar code using my laptop camera, but not with mobile or ipad.. Please suggest how can I ? App is asking to Enter bar code manually. Appreciate help.

    Thanks
    Shashank

    (0) 
    1. venkatasatishreddy karri

      Hai,

      I am getting “get user media is not defined” while run in tab and android. but it works on browser.

      Can u tell me how to solve the issue for  opening the camera in android and tablet.

      thanks and regards,

      vamsi

      (0) 
  3. sap ui

    Hey need help please I have this erreur :

    Uncaught TypeError: Cannot read property ‘setAttribute’ of undefined

    (0) 

Leave a Reply