Peripherals Integration Guide


Printing

To print, the application must first bind to the print service, then once bound, can use the APIs from the print service.

 @startuml
 !include uml_styles.iuml
 hide footbox

 participant POS order 10
 participant "Print Service" as PrintService order 20
 activate POS
 |||
 POS ->> PrintService: Bind to service
 activate PrintService
 POS <<- PrintService: Service connected
 ...
 POS ->> PrintService: Print
 POS <<- PrintService: Print started
 POS <<- PrintService: Print complete
 ...
 POS ->> PrintService: Unbind
 deactivate PrintService
 |||
 @enduml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/** Manages the connection to the service. */
private final ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        // We must use this to expose the service methods.
        mPrintService = IDirectPrintService.Stub.asInterface(iBinder);
        Log.d("PrintConnection", "Print service connected.");
    }
    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.d("PrintConnection", "Print service disconnected.");
        mPrintService = null;
    }
};


/** Binds to the direct print service by dynamically locating it in the system. */
private static boolean bindToDirectPrintService(Context context, ServiceConnection serviceConnection) {
    Intent directPrintImplicitIntent = new Intent("com.verifone.intent.action.DIRECT_PRINT");
    directPrintImplicitIntent.addCategory("com.verifone.intent.category.DIRECT_PRINT");
    Intent directPrintIntent = createExplicitFromImplicitIntent(context, directPrintImplicitIntent);
    return directPrintIntent != null && context.bindService(directPrintIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}

/** Used when binding to the print service to turn a dynamic intent into an explicit intent. */
@Nullable
private static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
    //Retrieve all services that can match the given intent
    PackageManager pm = context.getPackageManager();
    List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

    //Make sure only one match was found
    if (resolveInfo == null || resolveInfo.size() != 1) {
        Log.e(TAG, "Could not find service for intent with action " + implicitIntent.getAction() + ".");
        return null;
    }

    //Get component info and create ComponentName
    ResolveInfo serviceInfo = resolveInfo.get(0);
    String packageName = serviceInfo.serviceInfo.packageName;
    String className = serviceInfo.serviceInfo.name;
    ComponentName component = new ComponentName(packageName, className);

    //Create a new intent. Use the old one for extras and such reuse
    Intent explicitIntent = new Intent(implicitIntent);
    //Set the component to be explicit
    explicitIntent.setComponent(component);
    return explicitIntent;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/** Manages the connection to the service.  */
private val mServiceConnection = object : ServiceConnection {
    override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
        // We must use this to expose the service methods.
        mPrintService = IDirectPrintService.Stub.asInterface(iBinder)
        Log.d("PrintConnection", "Print service connected.")
    }

    override fun onServiceDisconnected(componentName: ComponentName) {
        Log.d("PrintConnection", "Print service disconnected.")
        mPrintService = null
    }
}


/** Binds to the direct print service by dynamically locating it in the system.  */
private fun bindToDirectPrintService(
    context: Context,
    serviceConnection: ServiceConnection
): Boolean {
    val directPrintImplicitIntent = Intent("com.verifone.intent.action.DIRECT_PRINT")
    directPrintImplicitIntent.addCategory("com.verifone.intent.category.DIRECT_PRINT")
    val directPrintIntent = createExplicitFromImplicitIntent(context, directPrintImplicitIntent)
    return directPrintIntent != null && context.bindService(
        directPrintIntent,
        serviceConnection,
        Context.BIND_AUTO_CREATE
    )
}

/** Used when binding to the print service to turn a dynamic intent into an explicit intent.  */
@Nullable
private fun createExplicitFromImplicitIntent(context: Context, implicitIntent: Intent): Intent? {
    //Retrieve all services that can match the given intent
    val pm = context.getPackageManager()
    val resolveInfo = pm.queryIntentServices(implicitIntent, 0)

    //Make sure only one match was found
    if (resolveInfo == null || resolveInfo!!.size != 1) {
        Log.e(TAG, "Could not find service for intent with action " + implicitIntent.action + ".")
        return null
    }

    //Get component info and create ComponentName
    val serviceInfo = resolveInfo!!.get(0)
    val packageName = serviceInfo.serviceInfo.packageName
    val className = serviceInfo.serviceInfo.name
    val component = ComponentName(packageName, className)

    //Create a new intent. Use the old one for extras and such reuse
    val explicitIntent = Intent(implicitIntent)
    //Set the component to be explicit
    explicitIntent.component = component
    return explicitIntent
}
1
/** Manages the connection to the service. */
1
/** Manages the connection to the service. */

Here’s a sample print listener that receives back the different events for printing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * The listener which receives the callbacks from the print service for print job status
 * updates.
 */
private final IDirectPrintListener mPrintListener = new IDirectPrintListener.Stub() {
    /** Called when a print job has moved from the queue and is being processed. */
    @Override
    public void started(String printId) throws RemoteException {

    }

    /** Called when the print job cannot continue, but could be resumed later. */
    @Override
    public void block(String printId, String errorMessage) throws RemoteException {

    }

    /** Called when the print job has finished being cancelled. This is the final message. */
    @Override
    public void cancel(String printId) throws RemoteException {

    }

    /** Called when the print job has failed, and cannot be resumed. This is the final message. */
    @Override
    public void failed(String printId, String errorMessage) throws RemoteException {

    }

    /** Called when the print job is complete. */
    @Override
    public void complete(String printId) throws RemoteException {

    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* The listener which receives the callbacks from the print service for print job status
* updates.
*/
private val mPrintListener = object : IDirectPrintListener.Stub() {
    /** Called when a print job has moved from the queue and is being processed.  */
    @Throws(RemoteException::class)
    fun started(printId: String) {

    }

    /** Called when the print job cannot continue, but could be resumed later.  */
    @Throws(RemoteException::class)
    fun block(printId: String, errorMessage: String) {

    }

    /** Called when the print job has finished being cancelled. This is the final message.  */
    @Throws(RemoteException::class)
    fun cancel(printId: String) {

    }

    /** Called when the print job has failed, and cannot be resumed. This is the final message.  */
    @Throws(RemoteException::class)
    fun failed(printId: String, errorMessage: String) {

    }

    /** Called when the print job is complete.  */
    @Throws(RemoteException::class)
    fun complete(printId: String) {

    }
}
/**
  * The listener which receives the callbacks from the print service for print job status
  * updates.
  */
/**
  * The listener which receives the callbacks from the print service for print job status
  * updates.
  */

An example of printing a receipt once the service is bound and the listener is set up.

1
2
3
4
5
try {
    mPrintService.printString(mPrintListener, receipt.getAsHtml(), null, Printer.PRINTER_FULL_CUT);
} catch (RemoteException e) {
    e.printStackTrace();
}
1
2
3
4
5
try {
    mPrintService.printString(mPrintListener, receipt.getAsHtml(), null, Printer.PRINTER_FULL_CUT);
} catch (RemoteException e) {
    e.printStackTrace();
}
1
/** Code to be added */
1
/** Code to be added */

Barcode Scanning

Barcode scanning APIs are available for scanning if the BYOD device has a Camera. To setup the scanner first add a listener PaymentSdk.initScanListener(scannerListener) to receive the scan results. Then the scanning can be started using PaymentSdk.startBarcodeScanner(attributes). There are a few attributes like scan formats, display feed, scan area, front/rear camera etc that you can pass to the API. The scan formats are listed under the ScannerBarcodeFormatEnum enumeration. For Android the attributes must contain an Activity or Fragment under the ScannerConfiguration.ATTRIBUTE_DISPLAY_FEED_PARENT key to work. The listener that was setup will receive the results as they are scanned.

See also

  • PaymentSdk.initScanListener(...)

  • PaymentSdk.startBarcodeScanner(...)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
private final ScannerListener mScannerListener = new ScannerListener() {
    @Override
    public void onBarcodeResult(String status, @Nullable HashMap<String, Object> attributes) {
        String barcode = "";
        String barcodeType = "";

        if (status == STATUS_BARCODE_DETECTED) {
            if (attributes.containsKey(ATTRIBUTE_BARCODE)) {
                barcode = (String) attributes.get(ATTRIBUTE_BARCODE);
            }
            if (attributes.containsKey(ATTRIBUTE_BARCODE_FORMAT)) {
                barcodeType = (String) attributes.get(ATTRIBUTE_BARCODE_FORMAT);
            }
            Log.d(TAG, "Scanner status:" + status + barcode + "-" + barcodeType);
        } else {
            Log.d(TAG, "Scanner status:" + status);
        }
    }
};

void initScanner() {
    mPsdk.initScanListener(mScannerListener);
    HashMap<String, Object> attributes = new HashMap<>();
    // Display the scanner at 90% of display view
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    Rect rect = new Rect((int)((float)metrics.widthPixels * .1f),
            (int)((float)metrics.heightPixels * .1f),
            (int)((float)metrics.widthPixels * .9f),
            (int)((float)metrics.heightPixels * .9f));
    attributes.put(ScannerConfiguration.ATTRIBUTE_SCAN_AREA_LIMIT, rect);
    attributes.put(ScannerConfiguration.ATTRIBUTE_SET_DIRECTION, 2);
    attributes.put(ScannerConfiguration.ATTRIBUTE_ACTIVATE_LIGHT, false);
    attributes.put(ScannerConfiguration.ATTRIBUTE_PLAY_SOUND, true);
    attributes.put(ScannerConfiguration.ATTRIBUTE_DISPLAY_FEED_PARENT, this);
    // Optionally limit the barcode formats. Leaving this empty
    // will search for the default list of barcodes.
    ScannerBarcodeFormatEnum[] scanFormats = new ScannerBarcodeFormatEnum[] {
            ScannerBarcodeFormatEnum.UPCA,
            ScannerBarcodeFormatEnum.UPCE,
            ScannerBarcodeFormatEnum.QRCODE
    };
    attributes.put(ScannerConfiguration.ATTRIBUTE_SCANNING_FORMATS, scanFormats);
    mPsdk.startBarcodeScanner(attributes);
}

Following dependencies need to be added to the application’s build.gradle.
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.google.zxing:core:3.4.0'
    implementation 'androidx.preference:preference-ktx:1.1.1'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
private val mScannerListener = ScannerListener { status, attributes ->
    var barcode: String? = ""
    var barcodeType: String? = ""
    if (status === STATUS_BARCODE_DETECTED) {
        if (attributes.containsKey(ATTRIBUTE_BARCODE)) {
            barcode = attributes[ATTRIBUTE_BARCODE] as String?
        }
        if (attributes.containsKey(ATTRIBUTE_BARCODE_FORMAT)) {
            barcodeType = attributes[ATTRIBUTE_BARCODE_FORMAT] as String?
        }
        Log.d(TAG, "Scanner status:$status$barcode-$barcodeType")
    } else {
        Log.d(TAG, "Scanner status:$status")
    }
}

fun initScanner() {
    mPsdk.initScanListener(mScannerListener)
    val attributes: HashMap<String, Any> = HashMap()
    // Display the scanner at 90% of display view
    val metrics = resources.displayMetrics
    val rect = Rect(
            (metrics.widthPixels.toFloat() * .1f).toInt(),
            (metrics.heightPixels.toFloat() * .1f).toInt(),
            (metrics.widthPixels.toFloat() * .9f).toInt(),
            (metrics.heightPixels.toFloat() * .9f).toInt()
    )
    attributes[ScannerConfiguration.ATTRIBUTE_SCAN_AREA_LIMIT] = rect
    attributes[ScannerConfiguration.ATTRIBUTE_SET_DIRECTION] = 2
    attributes[ScannerConfiguration.ATTRIBUTE_ACTIVATE_LIGHT] = false
    attributes[ScannerConfiguration.ATTRIBUTE_PLAY_SOUND] = true
    attributes[ScannerConfiguration.ATTRIBUTE_DISPLAY_FEED_PARENT] = this
    // Optionally limit the barcode formats. Leaving this empty
    // will search for the default list of barcodes.
    val scanFormats = arrayOf(
            ScannerBarcodeFormatEnum.UPCA,
            ScannerBarcodeFormatEnum.UPCE,
            ScannerBarcodeFormatEnum.QRCODE
    )
    attributes[ScannerConfiguration.ATTRIBUTE_SCANNING_FORMATS] = scanFormats
    mPsdk.startBarcodeScanner(attributes)
}

Following dependencies need to be added to the application’s build.gradle.
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.google.zxing:core:3.4.0'
    implementation 'androidx.preference:preference-ktx:1.1.1'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//scanner options
var config = [String: Any]()
config[VFIScannerConfigurationATTRIBUTEPLAYSOUND] = true
config[VFIScannerConfigurationATTRIBUTEACTIVATELIGHT] = true
config[VFIScannerConfigurationATTRIBUTECONTINUOUSSCAN] = true
config[VFIScannerConfigurationATTRIBUTESETDIRECTION] = Int(2)

//psdk scanner interface
sdk.initiateScan(view: scanWindowView, config: config, completion: { (result, error) in
    if let e = error {
        switch e {
        case .Cancel:
            //user has cancelled, therefore exit the scanner mode
        case .Exit:
            //if the continuous scan was false, an exit will be called after scanning item
        default:
            // handle the error case here
        }
        return
    }
    guard let data = result else {
        // handle nil result
        return
    }
    // handle the data
})
   barcode_received_event_handler_ = async (sender, args) =>
   {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
            () =>
            {
                StringBuilder builder = new StringBuilder("Found " + args.Count + " barcodes: \n\n");
                foreach (BarcodeScanResult result in args)
                {
                    builder.Append("Barcode Type: " + result.Type + " Barcode Text: " + result.Text + "\n");
                }
            });
    };

    ScannerPreview.OnBarcodeReceived += barcode_received_event_handler_;

    private void BarcodeScanner_Start()
    {
         ScannerPreview.StartPreview(new ScannerConfig());
    }

    private void BarcodeScanner_Stop()
    {
         ScannerPreview.StopPreview();
    }


.. code-tab:: c# .NET
   :linenos:

   // To be defined