MiSnapView
The MiSnapView provides a simple way to start and manage a session. The view automatically scales its size to match the camera preview dimensions, and provides camera control and access to analysis results.
NOTE: The frame analysis is stopped automatically by this view ON_PAUSE, but it is responsibility of the integrator to restart the MiSnap session when necessary.
Samples
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import com.miteksystems.misnap.apputil.LicenseFetcher
import com.miteksystems.misnap.R
import com.miteksystems.misnap.controller.MiSnapController
import com.miteksystems.misnap.controller.MiSnapController.ErrorResult
import com.miteksystems.misnap.controller.MiSnapController.FrameResult
import com.miteksystems.misnap.core.MiSnapSettings
import com.miteksystems.misnap.core.MibiData
import com.miteksystems.misnap.workflow.view.MiSnapView
fun main() {
//sampleStart
/**
* This example demonstrates an integration with the MiSnap SDK that does not use the built-in
* fragments and as such allows developers to roll a completely custom UI/UX.
*
* This example makes use of a [MiSnapView] defined in an XML layout to simplify the orchestration
* of setting up a camera, a [MiSnapController], and the science to analyze preview frames.
*
* NOTE: Ensure that the provided license has all the necessary features enabled for the target
* MiSnap session.
*
* @see com.miteksystems.misnap.examples.settings for examples on how to customize the UI/UX before
* deciding to use this example.
* @see com.miteksystems.misnap.examples.misnapview.MiSnapViewCode for an example on how to programmatically
* build an use a [MiSnapView].
*/
class MiSnapViewXml : Fragment(R.layout.example_misnap_view_xml) {
/**
* Fetch the Misnap SDK license.
* Good practice: Handle the license in a way that it is remotely updatable.
*/
private val license by lazy {
LicenseFetcher.fetch()
}
private lateinit var settings: MiSnapSettings
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settings = MiSnapSettings(MiSnapSettings.UseCase.ID_FRONT, license).apply {
analysis.document.trigger = MiSnapSettings.Analysis.Document.Trigger.AUTO
}
/**
* Optionally define a [MibiData] session outside of the view's lifecycle.
*/
MibiData.startSession(this::class.java.name, settings)
/**
* Get a [MiSnapView] by the XML element ID and observe the various [LiveData]s to get
* feedback results, final results, recorded videos, errors, camera events, etc.
*
* @see [R.layout.example_misnap_view_xml] for the specific XML attributes used to configure the [MiSnapView].
* @see [MiSnapView] for the full list of available APIs.
*/
view.findViewById<MiSnapView>(R.id.misnapView)
.also { misnapView ->
/**
* Observe the [MiSnapView.feedbackResult] [LiveData] to handle the feedback from the
* analyzed preview frames and handle them accordingly, e.g. by showing the corresponding
* instructions on screen based on the [MiSnapController.FeedbackResult.userAction],
* showing the detected document corners using [MiSnapController.FeedbackResult.corners]
* or the detected glare corners in [MiSnapController.FeedbackResult.glareCorners].
*/
misnapView.feedbackResult.observe(viewLifecycleOwner) { feedbackResult ->
}
/**
* Observe the [MiSnapView.finalFrame] [LiveData] to handle the successful results of
* a session, e.g. by collecting the captured frame bytes in [FrameResult.DocumentAnalysis.frame] to
* send the image to the server.
*
* @see [FrameResult] for all the possible result types emitted.
*/
misnapView.finalFrame.observe(viewLifecycleOwner) { result ->
// Handle the result depending on the session type.
when (result) {
is FrameResult.DocumentAnalysis -> {
}
else -> {}
}
}
/**
* Observe the [MiSnapView.controllerErrors] [LiveData] to handle errors during the
* analysis of the preview frames.
*
* @see [ErrorResult] for all the possible error types emitted.
*/
misnapView.controllerErrors.observe(viewLifecycleOwner) { result ->
when (result) {
is ErrorResult.DocumentDetection -> {
}
is ErrorResult.DocumentAnalysis -> {
}
else -> {}
}
}
/**
* Observe the [MiSnapView.recordedVideo] [LiveData] to receive a [ByteArray] with
* the contents of the recorded video if requested in [MiSnapSettings].
*/
misnapView.recordedVideo.observe(viewLifecycleOwner) { videoBytes ->
}
}
}
override fun onResume() {
super.onResume()
/**
* Start the camera preview and register a callback to know when the preview has
* started.
*/
requireView().findViewById<MiSnapView>(R.id.misnapView)
.startMiSnapSession(settings, viewLifecycleOwner) {
}
}
override fun onDestroyView() {
super.onDestroyView()
/**
* Optionally end the [MibiData] session outside of the view's lifecycle.
*/
MibiData.releaseSession(this::class.java.name)
}
}
//sampleEnd
}
import android.os.Bundle
import android.view.View
import android.view.animation.AnimationUtils
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import com.miteksystems.misnap.apputil.LicenseFetcher
import com.miteksystems.misnap.R
import com.miteksystems.misnap.camera.frameproducers.FrameProducer.Event
import com.miteksystems.misnap.controller.MiSnapController
import com.miteksystems.misnap.controller.MiSnapController.ErrorResult
import com.miteksystems.misnap.core.MiSnapSettings
import com.miteksystems.misnap.core.MibiData
import com.miteksystems.misnap.core.UserAction
import com.miteksystems.misnap.databinding.ExampleDocumentAnalysisBinding
import com.miteksystems.misnap.workflow.fragment.MiSnapWorkflowViewModel
import com.miteksystems.misnap.workflow.util.ViewBindingUtil
import com.miteksystems.misnap.workflow.view.*
fun main() {
//sampleStart
/**
* This example demonstrates an advanced integration that makes use of the different android Views
* included in the MiSnap SDK to fully customize the UI/UX by recreating an analysis screen.
* This is an advanced integration example and as such is only suitable for developers whose customization
* needs are not fulfilled through standard integration types customizations.
*
* This example uses the following android Views:
* * [MiSnapView] to orchestrate the acquisition of frames from the device's camera and analyze them.
* * [GuideView] to represent a visual guide for the document's alignment.
* * [HintView] to display hint messages to help the user during the session.
* * [TorchView] to facilitate the use of the device's camera torch light.
* * [RecordingIconView] to facilitate the awareness of an ongoing video recording during the session.
* * [SuccessView] to indicate that the session has concluded successfully.
*
* NOTE: Ensure that the provided license has all the necessary features enabled for the target
* MiSnap session.
*
* @see com.miteksystems.misnap.examples.misnapview for an in-depth example on the usage of the
* [MiSnapView] APIs.
* @see com.miteksystems.misnap.workflow.view for more information on the android views used in this
* example.
* @see com.miteksystems.misnap.examples.settings for examples on how to easily customize the UI and
* behavior of the SDK.
*/
class AnalysisFragment : Fragment(R.layout.example_document_analysis) {
/**
* Fetch the Misnap SDK license.
* Good practice: Handle the license in a way that it is remotely updatable.
*/
private val license by lazy {
LicenseFetcher.fetch()
}
/**
* To accurately track the number of auto and manual mode session retries in [MibiData] it is
* required to bind the session with [MibiData.bindSession] and to keep track of the fragment
* resumed state.
*/
private var resumed: Boolean = false
private var mibiDataSession = MibiData.bindSession()
private val binding by ViewBindingUtil.viewBinding(
this,
ExampleDocumentAnalysisBinding::bind
)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
/**
* Determine if the fragment is being resumed for [MibiData] tracking purposes.
*/
savedInstanceState?.let {
resumed = true
}
val settings = MiSnapSettings(MiSnapSettings.UseCase.PASSPORT, license).apply {
analysis.document.trigger = MiSnapSettings.Analysis.Document.Trigger.AUTO
// Optionally set the camera profile to use.
camera.profile = MiSnapSettings.Camera.Profile.DOCUMENT_BACK_CAMERA
}
/**
* Optionally define a [MibiData] session outside of the view's lifecycle.
*/
MibiData.startSession(this::class.java.name, settings)
/**
* Customize the look and feel of the [GuideView], e.g. by setting a custom drawable.
*
* @see [GuideView] for more information on the available properties that can be customized.
*/
binding.guideView.apply {
drawableId = R.drawable.custom_guideview
scale = 0.87f
}
/**
* Customize the look and feel of the [HintView], e.g. by setting a custom animation.
*/
binding.hintView.apply {
animation = AnimationUtils.loadAnimation(context, R.anim.hintview_animation)
duration = 2000
}
/**
* Observe the [MiSnapView.feedbackResult] [LiveData] to handle the feedback from the analyzed
* preview frames and handle them accordingly, e.g. by showing the corresponding instructions
* on screen based on the [MiSnapController.FeedbackResult.userAction].
*/
binding.misnapView.feedbackResult.observe(viewLifecycleOwner) {
/**
* Optional: specify a custom accessibility message for screen readers. By default it
* will use the message displayed.
*/
binding.hintView.contentDescription = getCustomAccessibilityMessage(it.userAction)
binding.hintView.text = it.userAction.toString()
}
/**
* Observe the [MiSnapView.finalFrame] [LiveData] to handle the successful results of a session
* and to perform UI cleanup actions, e.g. by stopping the video recording, clearing the contents
* of views and modifying their visibility.
*/
binding.misnapView.finalFrame.observe(viewLifecycleOwner) {
/**
* Finalize the recording of the session and clear the appropriate observers to stop
* receiving updates after a successful session.
*/
binding.misnapView.apply {
stopRecording()
feedbackResult.removeObservers(viewLifecycleOwner)
}
binding.recordingIconView.stop()
/**
* Adjust the visibility of the relevant android Views to indicate the user that the
* session has ended.
*/
binding.torchView.isVisible = false
binding.misnapView.isVisible = false
binding.guideView.isVisible = false
binding.hintView.apply {
clearText()
visibility = View.INVISIBLE
}
/**
* Display the [SuccessView] animation and finalize the session.
*/
binding.successView.start {
// Animation finished, time to end the session.
}
}
/**
* Observe the [MiSnapView.controllerErrors] [LiveData] to handle errors during the analysis
* of the preview frames, e.g. by aborting the session or addressing the error.
*/
binding.misnapView.controllerErrors.observe(viewLifecycleOwner) {
when (it) {
is ErrorResult.DocumentDetection, is ErrorResult.DocumentAnalysis -> {
}
else -> {}
}
}
/**
* Observe the [MiSnapView.cameraEvents] [LiveData] to handle errors during the initialization
* and operation of the camera, e.g. by aborting the session or addressing the error.
*/
binding.misnapView.cameraEvents.observe(viewLifecycleOwner) {
when (it) {
is Event.CameraInitialized -> {
// Make adjustments to the session based on the camera's capabilities.
// if not queried before
val cameraInfo = it.cameraInfo
if (!cameraInfo.supportsAutoAnalysis) {
}
}
is Event.InitializationError -> {
}
is Event.CameraReady -> {
}
is Event.VideoRecordingError -> {
}
else -> {}
}
}
/**
* Observe the [MiSnapView.torchEvents] [LiveData] to handle torch state changes, in this case
* by applying the new state to the [TorchView] so they're bound.
*/
binding.misnapView.torchEvents.observe(viewLifecycleOwner) {
if (null != it) {
binding.torchView.isTorchOn = it
}
}
/**
* Observe the [TorchView.torchEvents] [LiveData] to handle torch state change requests, in
* this case by applying the new state to the [MiSnapView] so that the request can reach the
* camera.
*/
binding.torchView.torchEvents.observe(viewLifecycleOwner) {
if (null != it) {
binding.misnapView.setTorchEnabled(it)
}
}
// Start the session.
binding.misnapView.startMiSnapSession(settings, viewLifecycleOwner) {
binding.recordingIconView.start()
/**
* Increase the number of auto or manual session retries based on the [MibiData.sessionOwner]
* and whether or not the fragment is being resumed.
*/
if ((MibiData.sessionOwner == this::class.java.name ||
MibiData.sessionOwner == MiSnapWorkflowViewModel::class.java.name) && !resumed
) {
if (settings.analysis.barcode.trigger == MiSnapSettings.Analysis.Barcode.Trigger.AUTO) {
mibiDataSession.metaData.autoTries += 1
} else {
mibiDataSession.metaData.manualTries += 1
}
}
}
}
override fun onDestroyView() {
super.onDestroyView()
/**
* Optionally end the [MibiData] session outside of the view's lifecycle.
*/
MibiData.releaseSession(this::class.java.name)
}
/**
* Gets an accessibility message given a [UserAction].
*/
private fun getCustomAccessibilityMessage(userAction: UserAction): String {
/**
* Map the user action to a custom accessibility message.
*/
return userAction.toString()
}
}
//sampleEnd
}
Constructors
Functions
Cancel focusing the camera.
Sets the new state of the torch and optionally registers a listener to the operation's result.
Focuses the camera, either at the provided point or at the center of the screen if no point is specified.
Starts a session with the provided settings. Required to start the camera and begin receiving feedback from image analysis.
Starts the recording of a video, the results will be posted to recordedVideo once the recording has been stopped with stopRecording.
Stops recording the camera's view. The video will be posted on recordedVideo.
Takes a picture which will be used in the final result. NOTE: Calling this method will disable the stream of feedbackResults, you must call startMiSnapSession to re-enable it.
Properties
Equivalent to providing the app:misnapViewBoundingBoxColor
attribute in the layout xml file.
LiveData for camera status events and errors.
LiveData for any errors raised during analysis.
LiveData containing per frame data for feedback to the user.
LiveData for the final result.
LiveData for tracking if the camera is focusing.
Equivalent to providing the app:misnapViewGlareBoxColor
attribute in the layout xml file.
LiveData containing the video after stopRecording has been called.
Equivalent to providing the app:misnapViewShowBoundingBox
attribute in the layout xml file.
Equivalent to providing the app:misnapViewShowGlareBox
attribute in the layout xml file.
LiveData for changes in the torch state, true for ON and false for OFF. Default Value: False.