Callbacks

Implementing behaviour for the callback parameter of the TireTreadScanView is optional, and only needed for advanced use-cases.

It allows your application to react to all of the scan events, and implement custom workflows around the scan process.

The available events are:

  • OnScanStarted: Invoked when the scanning process begins

  • OnScanStopped: Invoked when the scanning process ends

  • OnImageUploaded: Invoked after each frame is uploaded

  • OnDistanceChanged: Invoked whenever the distance between the device and the tire changes

    • This information serves as a guide to assist your app’s users in scanning correctly

  • OnTireWidthProvided: Invoked when the user provides a value via the TireWidthInput screen or skips the step entirely

Listen to the ScanEvents and handle them, e.g.:

  • Android

  • Swift

class MyScanActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_scan)

        val myTireTreadScanView = findViewById<TireTreadScanView>(R.id.tireTreadScanView)
        myTireTreadScanView.init(
            tireTreadScanViewConfig = TireTreadScanViewConfig(),
            onScanAborted = ::onScanAborted,
            onScanProcessCompleted = ::onScanProcessCompleted,
            tireTreadScanViewCallback = ::handleScanEvent,
        ) { measurementUUID, exception ->
            // handle error during scanning process
        }
    }

    private fun onScanAborted(measurementUUID: String?) {
        // handle scan aborted
    }

    private fun onScanProcessCompleted(measurementUUID: String) {
        Log.d("MY_APP", "upload is completed!")

        // redirect to the result loading screen
        if (!uuid.isNullOrEmpty()) {
            openLoadAndResultScreen(uuid)
        }
    }

    private fun handleScanEvent(event: ScanEvent) {
        when (event) {
            is OnImageUploaded -> {
                Log.i(
                    getString(R.string.app_name),
                    "onImageUploaded: ${event.uploaded}/${event.total}"
                )
            }

            // When not using the "default UI", use this event to provide guidance to the users
            is OnDistanceChanged -> onDistanceChanged(event.uuid, event.previousStatus, event.newStatus, event.previousDistance, event.newDistance)

            else -> {
                Log.i(getString(R.string.app_name), event.toString())
            }
        }
    }

    fun startScanning() {
        // this function allows your application to manually start the scan process
        TireTreadScanner.instance.startScanning()
    }

    fun stopScanning() {
        // this function allows your application to manually stop the scan process
        TireTreadScanner.instance.stopScanning()
    }

    fun abortScan() {
        // this function allows your application to manually abort the scan process
        TireTreadScanner.instance.abortScanning()
        finish()
    }

    private fun onDistanceChanged(uuid: String?, previousStatus: DistanceStatus, newStatus: DistanceStatus, previousDistance: Float, newDistance: Float, ) {
        super.onDistanceChanged(uuid, previousStatus, newStatus, previousDistance, newDistance)

        val measurementSystem = if (PreferencesUtils.shouldUseImperialSystem(this@ScanActivity)) {
            MeasurementSystem.Imperial
        } else {
            MeasurementSystem.Metric
        }

        // newDistance will be in millimeters if you choose Metric, or in inches, if you choose Imperial
        val distanceString: String = if (measurementSystem == MeasurementSystem.Imperial) {
            "$newDistance in"
        } else {
            "${(newDistance / 10).toInt()} cm"
        }

        var message = ""
        message = when (newStatus) {
            DistanceStatus.TOO_CLOSE -> {
                "Increase Distance: "
            }
            DistanceStatus.TOO_FAR -> {
                "Decrease Distance: "
            }
            else -> {
                "Distance OK"
            }
        }
        message += distanceString
        findViewById<TextView>(R.id.tvDistance).text = message
    }
}
extension YourViewController {

    TireTreadScanViewKt.TireTreadScanView(
        context: self,
        onScanAborted: onScanAborted,
        onScanProcessCompleted: onScanProcessCompleted,
        callback: handleScanEvent
        ) { measurementUUID, error in
        // Handle errors during scanning process
    }

    private func onError(measurementUUID: String?, exception: Exception) {
        print("onUploadFailed")
        self.displayError()
    }

    private func onScanAborted(measurementUUID: String?) {
        TireTreadScanner.companion.abortScanning()
        finish()
    }

    private func onScanProcessCompleted(measurementUUID: String) {
        print("onUploadCompleted")
        if let safeUuid = event.measurementUUID {
            // redirect to the result loading screen
            self.displayLoading(uuid: safeUuid)
        } else {
            self.displayError()
        }
    }

    private func handleScanEvent(event: ScanEvent) {
        switch(event) {

        case let event as OnImageUploaded:
            print("onImageUploaded: \(event.total) images uploaded in total")
            break

        // When not using the "default UI", use this event to provide guidance to the users
        case event as OnDistanceChanged:
            self.onDistanceChanged(event.measurementUUID, event.previousStatus, event.newStatus, event.previousDistance, event.newDistance)
            break
        default:
            print("ScanEvent: \(event.description)")
            break
        }
    }

    func startScanning() {
        // this function allows your application to manually start the scan process
        TireTreadScanner.companion.startScanning()
    }

    func stopScanning() {
        // this function allows your application to manually stop the scan process
        TireTreadScanner.companion.stopScanning()
    }

    func abortScanning() {
        // this function allows your application to manually abort the scan process
        TireTreadScanner.companion.abortScanning()
    }

    /// When not using the "default UI", use this callback to provide guidance to the users
    /// Called when the distance has changed.
    ///
    /// - Parameters:
    ///   - uuid: The UUID associated with the distance change.
    ///   - previousStatus: The previous distance status.
    ///   - newStatus: The new distance status.
    ///   - previousDistance: The previous distance value.
    ///   - newDistance: The new distance value.
    ///
    /// Note: The distance values are provided in millimeters if the metric system is selected (`UserDefaultsManager.shared.imperialSystem = false`), and in inches if the imperial system is selected (`UserDefaultsManager.shared.imperialSystem = true`).
    private func onDistanceChanged(uuid: String?, previousStatus: DistanceStatus, newStatus: DistanceStatus, previousDistance: Float, newDistance: Float) {
        if Int(newDistance) != Int(previousDistance) {
            let distanceInCentimeters = UserDefaultsManager.shared.imperialSystem ? (newDistance * 2.54) : (newDistance / 10.0)
            DispatchQueue.main.async { [weak self] in
                self?.updateUI(status: newStatus, distance: Int(UserDefaultsManager.shared.imperialSystem ? newDistance : distanceInCentimeters))
            }
        }
    }
}