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: 
WouterLemaire
Active Contributor

Introduction


It’s a question that has been asked often by customers and as well in the SAP community:

How can we implement a QR scanner into a Fiori app? Do we need to use a cordova plugin for this? Do we need to make a hybrid app?

The answer is simple, this can be done in Fiori without the need of a cordova plugin nor making it hybrid.

In the past it was not possible to use the camera directly from a web application on all mobile devices. For Android phones it is already possible for a while. iOS devices on the other hand had to wait until iOS 11. Even though this was already released a few years ago, I’m not sure everyone is aware of the fact that this means iOS supports WebRTC. Which again means, you can use the camera of your phone directly in Safari :). No struggling with Cordova plugins and hybrid apps 🙂

More information about WebRTC support for iOS:

https://flashphoner.com/ios-safari-11-now-supports-webrtc/

 

Solution


I created a demo application of a QR scanner inside a fiori app. Here you have a video on how it works.

 



 

If my trial account is still active you can try it yourself here: https://2d32c6fatrial-workspaces-ws-w6zlm-app2.eu10.trial.applicationstudio.cloud.sap/bewlScannerApp...

The demo app allows you to:

  • Select any available camera

  • Mirror the camera or not

  • Scan QR codes


How it works


First of all, I did not all implement this myself ? I used a great and easy to use library for this: Instascan. More information about the library can be found here: https://github.com/schmich/instascan .

The problem of this library is that it’s not well maintained. When you look at the pull requests, you’ll notice many un-merged pull requests. Some of them contain fixes for iOS… Therefore, I started from this Pull Request: https://github.com/CoyoteLeo/instascan

Next to Instascan, I also used a WebRTC adapter library: https://github.com/webrtc/adapter

This library will handle differences of WebRTC implementation in different browsers. You have to keep this library up to date in case a browser does an update of the WebRTC implementation.

 

I created a UI5 Library with a UI5 control for the QR Scanner. To do this, I’ve added the istanscan and webrtc library into my UI5 Library so I’m able to use them in my UI5 Control.

The renderer of the UI5 QR Scanner control is pretty basic, it contains a toolbar with:

  • A dropdown to switch cameras in case there is more than one (front and back on phone)

  • A button to enable or disable mirroring


(The toolbar is created with UI5 controls in the init function of the implementation code.)

After the toolbar comes the video html tag for showing the camera.

The renderer function is using the new control api:
var QRScannerRenderer = {
apiVersion: 2,
render: function (oRm, oControl) {
oRm.openStart("div", oControl).style("width", "100%").style("height", "100%").openEnd();
oRm.renderControl(oControl.getAggregation("_toolbar"));
oRm.openStart("video", oControl).style("width", "100%").style("height", "100%").openEnd().close("video");
oRm.close("div");
}
};

 

In the “onAfterRendering” function of the control I initialize the camera using the Instascan library. This is also the place where I bind it to the “video” tag in the renderer function. I’m using “lastElementChild” on “getDomRef” because I wrapped it together with the toolbar in a div and it’s the last element in that div.

Next, the code will assign an eventhandler to handle scanned objects. It will also get the available cameras to add it to the dropdownlist.

Last but not least, it will start the scanner using the first one it can find.
onAfterRendering: function (evt) {
this.scanner = new Instascan.Scanner({
video: this.getDomRef().lastElementChild,
mirror: false
});
this.scanner.addListener('scan', function(qrcode){
this.onQRCodeScanned(qrcode);
}.bind(this));
Instascan.Camera.getCameras().then(function (cameras) {
this.cameras = cameras;
cameras.forEach(function (camera, key) {
this._cameraSelection.addItem(new Item({ key: key, text: camera.name }));
}.bind(this))
if (cameras.length > 0) {
this.scanner.start(cameras[0]);
} else {
console.error('No cameras found.');
}
}.bind(this)).catch(function (e) {
console.error(e);
});
},

 

In the eventhandler to handle scanned object I update the property “value” to update bindings and fire the event so it can be catched in views/controllers:
onQRCodeScanned: function (qrcode) {
this.setProperty("value", qrcode, true);
this.fireQRCodeScanned({
value: qrcode
});
},

 

When another camera is selected in the dropdown it will start the scanner on the newly selected camera. For this I have implemented the following eventhandler in the control:
onChangeCamera: function (event) {
this.scanner.start(this.cameras[event.getParameter("selectedItem").getKey()]);
},

Depending on which camera you are using, it might be required to enable or disable mirroring. In case of using the back camera of your phone it should be fine without mirroring. In case you use the front camera, you might consider using mirroring. For this I’ve added a switch to the toolbar that will change the mirroring property:
onChangeMirror: function (event) {
this.scanner.mirror = event.getParameter("state");
}

 

The full project is available on GitHub: https://github.com/lemaiwo/UI5QRScannerApp

It is one mta that contains the UI5 Library with the UI5 QR Scanner control and a UI5 Demo app.

The UI5 Library with the UI5 QR SCanner:

https://github.com/lemaiwo/UI5QRScannerApp/tree/master/ScannerAppLibrary

The UI5 demo app as an example how to use this UI5 QR Scanner control in a UI5 App:

https://github.com/lemaiwo/UI5QRScannerApp/tree/master/ScannerApp

 

You can clone the full project into the Business Application Studio (BAS) and run it direclty in BAS or deploy it to CF and run it using the Central AppRouter. To support my library in BAS and the Central App I added the following in the component.js:

( I know this is not the best way to do this, but adding the library in the manifest in combination with resourceroots could make it work in BAS or with the central AppRouter. Both approuters (BAS vs Central) generate a different url. For that reason, I did it the "ugly" way to make it work in BAS and the central AppRouter by adding this check in the component.js. If running in BAS, load the library from the provided path in BAS, otherwise use the path that's provided by the Cetnral AppRouter. Any feedback to improve this is welcome!)
if(location.host.indexOf("studio")>-1){
//to make it work in app studio
sap.ui.getCore().loadLibrary("be.wl.ScannerAppLibrary", "/bewlScannerAppLibrary/be/wl/ScannerAppLibrary");
}else{
//to make it work in central approuter
sap.ui.getCore().loadLibrary("be.wl.ScannerAppLibrary", "/bewlscannerapp.bewlScannerAppLibrary/resources/be/wl/ScannerAppLibrary");
}

 

For running in BAS you also need to change your config to the following:
{
"name": "Run ScannerAppProject-ScannerApp",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/ScannerAppProject/node_modules/@sap/html5-repo-mock/index.js",
"args": [
"--standalone",
"/bewlScannerApp/index.html",
"--ui5",
"{\"version\":\".\"}"
],
"cwd": "${workspaceFolder}/ScannerAppProject/ScannerApp",
"env": {
"PORT": "6004",
"MOCK_LOOKUP_DIRS": "[\"webapp\",\"src\"]",
"run.config": "{\"handlerId\":\"ui5_run_config_handler_id\",\"runnableId\":\"/home/user/projects/ScannerAppProject/ScannerApp\"}"
},
"envFile": "${workspaceFolder}/ScannerAppProject/ScannerApp/.env1",
"preLaunchTask": "Build ScannerAppProject"
}

 

Watch carefully for “MOCK_LOOKUP_DIRS”. This has been extended to look for the “src” folder of the library as well:


On trial accounts the HTML5 app repo is not yet visible ( at least in my account... ) but you can find the list of deployed apps to the Central AppRouter by using the following command:
cf html5-list -u -d

3 Comments
Labels in this area