Skip to content

Android Native Bridge

Part of: MPAC SmartPOS Cloud Platform - Product RequirementsVersion: 2.0 Last Updated: 2026-01-28


Overview

The Android Native Bridge provides a bidirectional communication interface between the native Android application layer and the embedded WebView running terminal applications. It exposes device-specific capabilities (device info, hardware peripherals, external app launching) to JavaScript code while maintaining security and type safety through a well-defined JavascriptInterface.

Table of Contents


Bridge Architecture

Communication Flow:

WebView JavaScript ←→ MPACBridge (JavascriptInterface) ←→ Android Native Services

Key Components:

  • MPACBridge: Kotlin class annotated with @JavascriptInterface
  • WebView Configuration: Enables JavaScript and bridge injection
  • Activity Result Launchers: Handle asynchronous operations (QR scan, external apps)
  • Callback Mechanism: JavaScript functions invoked from native code

Bridge Interface Implementation

Core Bridge Class

kotlin
@JavascriptInterface
class MPACBridge(private val context: Context) {

    @JavascriptInterface
    fun getDeviceInfo(): String {
        return JSONObject().apply {
            put("device_id", getDeviceId())
            put("merchant_id", getMerchantId())
            put("store_id", getStoreId())
            put("battery_level", getBatteryLevel())
        }.toString()
    }

    @JavascriptInterface
    fun launchCreditCardApp(requestJson: String, callback: String) {
        val intent = Intent().apply {
            setClassName(
                "com.demo.smarttab.creditcard",
                "com.demo.smarttab.creditcard.MainActivity"
            )
            putExtra("request", requestJson)
        }
        creditCardLauncher.launch(intent)
        // Result will be sent via callback
    }

    @JavascriptInterface
    fun printReceipt(receiptData: String): Boolean {
        return printerService.print(receiptData)
    }

    @JavascriptInterface
    fun scanQR(callback: String): Boolean {
        qrScannerLauncher.launch(Intent(context, QRScanActivity::class.java))
        // Result sent via callback
        return true
    }
}

WebView Configuration

kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var webView: WebView
    private lateinit var bridge: MPACBridge

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        bridge = MPACBridge(this)

        webView = findViewById(R.id.webview)
        webView.settings.apply {
            javaScriptEnabled = true
            domStorageEnabled = true
            allowFileAccess = false
            allowContentAccess = false
        }

        // Inject bridge into WebView
        webView.addJavascriptInterface(bridge, "MPACBridge")

        webView.loadUrl("https://terminal.mpac-cloud.com")
    }
}

WebView Integration

Accessing Bridge from JavaScript

javascript
// In WebView JavaScript code

// Get device information
const deviceInfo = JSON.parse(window.MPACBridge.getDeviceInfo());
console.log('Device ID:', deviceInfo.device_id);
console.log('Merchant ID:', deviceInfo.merchant_id);
console.log('Store ID:', deviceInfo.store_id);
console.log('Battery Level:', deviceInfo.battery_level);

Launching External Payment App

javascript
// Launch credit card payment application
window.MPACBridge.launchCreditCardApp(
  JSON.stringify({
    amount: 10000,
    currency: 'JPY',
    transaction_id: 'TXN-12345'
  }),
  'handleCreditCardResult'
);

// Callback function invoked by native code
function handleCreditCardResult(resultJson) {
  const result = JSON.parse(resultJson);

  if (result.status === 'success') {
    console.log('Payment approved:', result.approval_code);
    updatePaymentStatus(result.approval_code);
  } else if (result.status === 'declined') {
    console.error('Payment declined:', result.reason);
    showErrorMessage(result.reason);
  } else if (result.status === 'error') {
    console.error('Payment error:', result.error_message);
    handlePaymentError(result);
  }
}

QR Code Scanning

javascript
// Initiate QR code scan
const scanResult = window.MPACBridge.scanQR('handleQRScanResult');

function handleQRScanResult(qrData) {
  console.log('Scanned QR:', qrData);

  // Parse and validate QR code
  try {
    const paymentData = JSON.parse(qrData);
    processPayment(paymentData);
  } catch (error) {
    console.error('Invalid QR code:', error);
  }
}

Receipt Printing

javascript
// Print receipt
const receiptData = JSON.stringify({
  store_name: 'My Store',
  transaction_id: 'TXN-12345',
  items: [
    { name: 'Coffee', price: 500, quantity: 2 },
    { name: 'Sandwich', price: 800, quantity: 1 }
  ],
  total: 1800,
  tax: 144,
  timestamp: new Date().toISOString()
});

const success = window.MPACBridge.printReceipt(receiptData);

if (success) {
  console.log('Receipt printed successfully');
} else {
  console.error('Printer error');
  showPrinterErrorDialog();
}

Available Methods

getDeviceInfo(): String

Retrieves device information and current operational context.

Returns: JSON string containing:

  • device_id: Unique device identifier
  • merchant_id: Associated merchant ID
  • store_id: Associated store ID
  • battery_level: Current battery percentage (0-100)

Example:

javascript
const info = JSON.parse(window.MPACBridge.getDeviceInfo());

launchCreditCardApp(requestJson: String, callback: String): void

Launches external credit card payment application via Intent.

Parameters:

  • requestJson: JSON string containing payment request data
  • callback: JavaScript function name to invoke with result

Result Format:

json
{
  "status": "success" | "declined" | "error",
  "approval_code": "123456",
  "transaction_id": "TXN-001",
  "reason": "Optional decline/error reason"
}

printReceipt(receiptData: String): Boolean

Prints receipt using device's built-in thermal printer.

Parameters:

  • receiptData: JSON string containing receipt data

Returns: true if print job queued successfully, false on error


scanQR(callback: String): Boolean

Opens camera for QR code scanning.

Parameters:

  • callback: JavaScript function name to invoke with scanned data

Returns: true if scanner launched, false on error

Callback Payload: Raw QR code string content


Security Considerations

Input Validation

All data passed from JavaScript to native code must be validated:

kotlin
@JavascriptInterface
fun launchCreditCardApp(requestJson: String, callback: String) {
    // Validate JSON structure
    val request = try {
        JSONObject(requestJson)
    } catch (e: JSONException) {
        invokeCallback(callback, """{"status":"error","message":"Invalid JSON"}""")
        return
    }

    // Validate required fields
    if (!request.has("amount") || !request.has("transaction_id")) {
        invokeCallback(callback, """{"status":"error","message":"Missing required fields"}""")
        return
    }

    // Proceed with validated data
    launchPaymentIntent(request, callback)
}

Callback Sanitization

Callback function names must be sanitized to prevent injection attacks:

kotlin
private fun invokeCallback(callback: String, data: String) {
    // Validate callback name (alphanumeric + underscore only)
    if (!callback.matches(Regex("^[a-zA-Z0-9_]+$"))) {
        Log.e(TAG, "Invalid callback name: $callback")
        return
    }

    val js = "$callback('${data.replace("'", "\\'")}')"
    runOnUiThread {
        webView.evaluateJavascript(js, null)
    }
}

HTTPS Enforcement

Only load WebView content from secure HTTPS endpoints:

kotlin
webView.webViewClient = object : WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
        if (request.url.scheme != "https") {
            Log.w(TAG, "Blocked non-HTTPS URL: ${request.url}")
            return true // Block HTTP requests
        }
        return false
    }
}

Permission Management

Bridge methods requiring sensitive permissions should check runtime permissions:

kotlin
@JavascriptInterface
fun scanQR(callback: String): Boolean {
    if (!hasCameraPermission()) {
        requestCameraPermission(callback)
        return false
    }
    launchQRScanner(callback)
    return true
}

See Also

Related Integration:

Domain Context:

Technical Implementation:

Code Reference:

  • Android Terminal Apps: /mpac-terminal-apps/

Navigation: ← Previous: Frontend SDK | ↑ Back to Integration Specifications | Next: External System Integrations →

MPAC — MP-Solution Advanced Cloud Service