Scan Process
The scanning process happens entirely through the TireTreadScanView
and should be performed in Landscape mode.
The TireTreadScanView
To start using the Anyline Tire Tread SDK for your measurements, you will need the TireTreadScanView
.
This view can be added directly to your Layout.xml file, or via code.
Layout
To include the TireTreadScanView
in your Layout.xml file, add the io.anyline.tiretread.sdk.scanner.TireTreadScanView
view in the appropriated hierarchy, e.g.:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:backgroundTint="@color/black"
tools:context=".activities.ScanActivity">
<io.anyline.tiretread.sdk.scanner.TireTreadScanView
android:id="@+id/tireTreadScanView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:measurementSystem="metric" />
</androidx.constraintlayout.widget.ConstraintLayout>
To configure the TireTreadScanView in a Layout, you can use the following attributes:
-
app:measurementSystem
-metric
orimperial
-
app:disableDefaultHaptic
- if true, the SDK will not make use of the device’s haptic during the scanning process to provide feedback to the users. -
app:disableDefaultUi
- if true, the scan view will not display any UI elements (except the Camera Preview).
Code
To instantiate the TireTreadScanView
via code, you can use the following snippet:
val ttrScanViewConfig = TireTreadScanViewConfig.Builder()
// .withMeasurementSystem(MeasurementSystem.Metric)
// .withCustomDefaultUiConfig(customUiConfg)
// .disableDefaultHaptic()
// .disableDefaultUi()
// .addAdditionalContext(additionalContext)
.build()
TireTreadScanView(context = this, config = ttrScanViewConfig, callback = this, onInitializationFailed = { ex -> /* handle */ })
This method can only be used from the context of a @Composable function. |
When initializing the TireTreadScanView via code, you have the possibility to set the behavior, appearance, and metadata of the scanning process. For that, you can use the following options:
-
withMeasurementSystem
-MeasurementSystem.Metric
orMeasurementSystem.Imperial
-
withCustomDefaultUiConfig
- allows the customization of the Default UI -
disableDefaultHaptic
- if set, the SDK will not make use of the device’s haptic during the scanning process to provide feedback to the users. -
disableDefaultUi
- if set, the scan view will not display the default UI elements (except the Camera Preview). -
addAdditionalContext
- provides additional context to a scan. Check below for more information.
Additional Context
The AdditionalContext
is an optional property which allows you to provide more context to a scan. This makes sense in a workflow, where a scan is connected to other TireTread scans or other information in a larger context.
Its initialization requires a TirePosition
.
The TirePosition
identifies the position where the tire is mounted on the vehicle, considering the primary direction of travel. E.g. the front left tire of a passenger car would be specified as axle=1, side=TireSide.Left, position_on_axle=1
and the rear right tire of a passenger car as axle=2
, side=TireSide.Right
, position_on_axle=1
The TirePosition
's parameters are as follows:
-
axle
: Number of the axle, whereas 1 is the front axle (in direction of travel). Its value must be at least 1. -
side
: Enum, representing the side where the Tire is located (Left, Center, Right). -
positionOnAxle
: Position on the axle, whereas 1 is the outermost tire and higher values represent the following inner tires. If the side is set tocenter
, 1 is the leftmost tire (in direction of travel), if there are multiple tires on the center axle. Its value must be at least 1.
If the AdditionalContext
is provided, it will also be returned with the results in the TreadDepthResult
object, in the measurement
property.
TirePosition initialization will throw an IllegalArgumentException if axle or positionOnAxle are 0.
|
Audio Feedback
The Tire Tread SDK can provide audio feedback to guide users throught the scan process.
To make use of these audio feedbacks, your application needs to provide (and thus can customize) the audio files inside its Assets
folder.
The audio feedbacks (with their respective files names) on Android are played on:
-
Scan Started
-
tiretread_sound_start.wav
-
-
Scan Stopped
-
tiretread_sound_stop.wav
-
-
Phone too close to the Tire
-
tiretread_sound_beep_too_close.wav
-
-
Phone too distant from the Tire
-
tiretread_sound_beep_too_far.wav
-
The SDK only supports these file names, and the .wav
extension.
An example implementation, and the example audio files, can be found in our GitHub Example implementation.
To disable the audio feedback, remove these audio files from the applications' Assets folder or rename them. |
If you have issues with the disposal of the TireTreadScanView, add the following lines to your Activity’s code, to define the View Composition Strategy:
override fun onCreate(savedInstanceState: Bundle?) {
...
val myTireTreadScanView = findViewById<TireTreadScanView>(R.id.myTireTreadScanView)
myTireTreadScanView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(this))
...
}
Make sure that your "ScanActivity" runs in Landscape mode. |
Start Scanning
In the previous example, we are not disabling the Default UI (disableDefaultUi
) in the TireTreadScanView. This means that the ScanView will draw and control all the required components for the scan process.
When using the default UI, your application only needs to implement behavior in a few callbacks from the SDK: onScanAbort
, onUploadCompleted
, onUploadFailed
, and onUploadAborted
. Jump to the Handling the SDK’s events section if you are using the default UI.
When not using the default UI, your application can define the UX of the entire scan process. For that, start by adding a button alongside with the TireTreadScanView. This button will be responsible for starting and stopping the scan process. Here is a Kotlin example with its functionality:
fun onClickedBtnStartScan(view: View) {
val scannerInstance = TireTreadScanner.instance
val btnStartScan = (view as Button)
if (!scannerInstance.isScanning) {
// Here we start scanning
scannerInstance.startScanning()
btnStartScan.text = "Stop"
} else {
btnStartScan.visibility = View.GONE
btnStartScan.isEnabled = false
btnStartScan.isClickable = false
// Here we stop scanning
scannerInstance.stopScanning()
}
}
The TireTreadScanner.instance
also provides the captureDistanceStatus
property, with which your application can check if the user is positioning the device at the correct distance from the tire. This property can be used to prevent scans from initiating at an incorrect distance, e.g.:
if (TireTreadScanner.instance.captureDistanceStatus == DistanceStatus.OK)
TireTreadScanner.instance.startScanning()
else {
Toast.makeText(
this,
"Move the phone to the correct position before starting.",
Toast.LENGTH_SHORT
).show()
}
The scanning process can be stopped at any time. If not manually stopped, the scan process is automatically stopped after 10 seconds. |
Besides the "Start/Stop" button, when not using the default UI, add also an "Abort" button (or e.g., an 'x' icon), through which the users can abort the Scan Process without waiting for the results. This button should call the TireTreadScanner.instance.abortScanning()
function.
Handling the SDK’s events
The TireTreadScanViewCallback
is the interface through which the TireTreadScanView
communicates back to your application. With these callbacks, you can react on changes and implement your own workflow around the scan process. The callbacks available are:
-
onScanStart
: Invoked when the scanning process begins -
onScanStop
: Invoked when the scanning process ends -
onScanAbort
: Invoked when the scanning process is aborted-
Should be implemented also when using the default UI
-
-
onImageUploaded
: Invoked after each frame is uploaded -
onUploadCompleted
: Invoked once all the frames were successfully uploaded for processing-
Should be implemented also when using the default UI
-
-
onUploadAborted
: Invoked when the upload process is aborted.-
Should be implemented also when using the default UI
-
-
onUploadFailed
: Invoked if any issue has occurred during upload-
Should be implemented also when using the default UI
-
-
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
-
The callback "onFocusFound" will not be invoked - and should be used - on Android. |
Once your "myTireTreadScanView" object is available, add your callback implementation to it. In this example, we will use our own Activity as the callback implementation.
Implement the TireTreadScanViewCallback
interface, and override the desired methods, e.g.:
// Implement the TireTreadScanViewCallback interface
class MyScanActivity : AppCompatActivity(), TireTreadScanViewCallback {
private var aborted = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scan)
val myTireTreadScanView = findViewById<TireTreadScanView>(R.id.tireTreadScanView)
myTireTreadScanView.scanViewCallback = this
}
// These callbacks should always be implemented
override fun onScanAbort(uuid: String?) {
super.onScanAbort(uuid)
Log.d("MY_APP", "scan was aborted!")
aborted = true
// Leave the scan activity
finish()
}
override fun onUploadCompleted(uuid: String?) {
super.onUploadCompleted(uuid)
Log.d("MY_APP", "upload is completed!")
// redirect to the result loading screen
if (!aborted && !uuid.isNullOrEmpty()) {
openLoadAndResultScreen(uuid)
}
}
override fun onUploadAborted(uuid: String?) {
super.onUploadAborted(uuid)
mainHandler.post {
Toast.makeText(applicationContext, "Upload Aborted", Toast.LENGTH_SHORT).show()
}
finish()
}
override fun onUploadFailed(uuid: String?, exception: Exception) {
super.onUploadFailed(uuid, exception)
Log.e("MY_APP", "onUploadFailed: ", exception)
Toast.makeText(applicationContext, "Upload Failed. Check your internet and try again.", Toast.LENGTH_SHORT).show()
finish()
}
// When not using the 'default UI', you will need to implement (at least) the following functions to control the scan process
fun startScanning(view: View) {
TireTreadScanner.instance.startScanning()
}
fun stopScanning() {
TireTreadScanner.instance.stopScanning()
}
fun abortScan() {
TireTreadScanner.instance.abortScanning()
finish()
}
// When not using the "default UI", use this callback to provide guidance to the users
override fun onDistanceChanged(uuid: String?, previousStatus: DistanceStatus, newStatus: DistanceStatus, previousDistance: Float, newDistance: Float,
) {
super.onDistanceChanged(uuid, previousStatus, newStatus, previousDistance, newDistance)
// newDistance will be in millimiters if you choose Metric, or in inches, if you choose Imperial
val scanView = findViewById<TireTreadScanView>(R.id.tireTreadScanView)
val measurementSystem = scanView.measurementSystem
val distanceString: String = if(measurementSystem == MeasurementSystem.Imperial){
"${inchStringToTriple(inchToFractionString(newDistance.toDouble())).first} 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
}
}
You can check out our GitHub example repository for a full implementation of the Scan Activity. |
Check out the next section to learn how to handle the measurement results after the scan process is finished.