MiSnapWorkflowViewModel
Used for storing inter-fragment state and data during a session, this view model is designed to work within the scope of an activity and the fragments that pertain to a single session, hence if working directly with this class it is necessary to acquire it through the activity's androidx.lifecycle.ViewModelProvider.
Samples
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentContainerView
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModelProvider
import com.miteksystems.misnap.apputil.LicenseFetcher
import com.miteksystems.misnap.R
import com.miteksystems.misnap.core.MiSnapSettings
import com.miteksystems.misnap.workflow.MiSnapErrorResult
import com.miteksystems.misnap.workflow.MiSnapFinalResult
import com.miteksystems.misnap.workflow.fragment.MiSnapWorkflowViewModel
fun main() {
//sampleStart
/**
* This example demonstrates an activity-less integration of the MiSnap SDK using Jetpack Navigation
* navgraphs that is best suited for developers that follow the single-activity architecture and
* don't want to launch another activity to invoke the MiSnap SDK.
*
* This example uses a [FragmentContainerView] that hosts a navgraph that in turn includes the built-in
* [R.navigation.barcode_session_flow] navgraph to take care of the navigation for a barcode session
* while making use of the [MiSnapWorkflowViewModel] to configure the session.
*
* NOTE: When working with the [MiSnapWorkflowViewModel] it is important to ensure that the view model
* is acquired through the activity's [ViewModelProvider].
*
* NOTE: Ensure that the provided license has all the necessary features enabled for the target
* MiSnap session.
*
* @see R.navigation.example_session_navigation for the navgraph definition and setup.
*
* @see com.miteksystems.misnap.examples.fragment.AnalysisFragmentTransaction for an activity-less
* integration that doesn't use Jetpack Navigation and drives the navigation with [FragmentTransaction]s.
*/
class AnalysisFragmentNavigation : AppCompatActivity(R.layout.example_fragment_navigation) {
/**
* 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()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/**
* Build a [MiSnapSettings] object and apply it to the [MiSnapWorkflowViewModel] to use it
* during the session.
* The navigation in this example is handled by the [FragmentContainerView] in the XML layout
* of this activity, it is important to apply the settings before navigating to the start
* destination of the navgraph.
*/
val settings = MiSnapSettings(MiSnapSettings.UseCase.BARCODE, license).apply {
analysis.barcode.trigger = MiSnapSettings.Analysis.Barcode.Trigger.AUTO
}
ViewModelProvider(this)[MiSnapWorkflowViewModel::class.java].also { viewModel ->
viewModel.applySettings(settings)
/**
* Observe the [MiSnapWorkflowViewModel.results] [LiveData] to receive the [MiSnapFinalResult]s
* and handle them accordingly, e.g. by collecting the results or re-configuring the
* [MiSnapWorkflowViewModel] for another session.
*/
viewModel.results.observe(this) {
it?.let { result ->
when (result) {
is MiSnapFinalResult.BarcodeSession -> {
}
else -> {}
}
// Once you're done handling the results, clear them before invoking a new session.
viewModel.clearResults()
}
}
/**
* Observe the [MiSnapWorkflowViewModel.error] [LiveData] to receive [MiSnapErrorResult]s
* and handle them accordingly.
*/
viewModel.error.observe(this) {
it?.let {
}
}
}
}
}
//sampleEnd
}
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.miteksystems.misnap.apputil.LicenseFetcher
import com.miteksystems.misnap.core.MiSnapSettings
import com.miteksystems.misnap.databinding.ExampleFragmentTransactionBinding
import com.miteksystems.misnap.workflow.MiSnapErrorResult
import com.miteksystems.misnap.workflow.MiSnapFinalResult
import com.miteksystems.misnap.workflow.MiSnapWorkflowError
import com.miteksystems.misnap.workflow.fragment.*
fun main() {
//sampleStart
/**
* This example demonstrates an activity-less integration of the MiSnap SDK using [FragmentTransaction]s
* that is best suited for developers that follow the single-activity architecture, that don't want
* to launch another activity to invoke the MiSnap SDK and that can't use Jetpack Navigation.
*
* Internally, the MiSnap SDK will always use Jetpack Navigation navgraphs to orchestrate the navigation
* between fragments. When it's not possible to use Jetpack Navigation, the SDK will emit a
* [NavigationError] through the [MiSnapWorkflowViewModel.navigationErrors] [LiveData] with the appropriate
* context so that the navigation can be handled manually, in this case, through [FragmentTransaction]s.
*
* NOTE: When working with the [MiSnapWorkflowViewModel] it is important to ensure that the view model
* is acquired through the activity's [ViewModelProvider].
*
* NOTE: Ensure that the provided license has all the necessary features enabled for the target
* MiSnap session.
* @see com.miteksystems.misnap.workflow.fragment for the list of available fragments included in the
* MiSnap SDK.
* @see com.miteksystems.misnap.examples.fragment.AnalysisFragmentNavigation for a simpler activity-less
* integration that doesn't use [FragmentTransaction]s.
*/
class AnalysisFragmentTransaction : AppCompatActivity() {
/**
* 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 binding: ExampleFragmentTransactionBinding
private var tutorialFragmentHashCode: Int? = null
private val viewModel by lazy {
ViewModelProvider(this)[MiSnapWorkflowViewModel::class.java]
}
/**
* Handle the incoming [NavigationError]s to determine the next destination and drive the navigation
* using [FragmentTransaction]s.
*
* Use the [NavigationErrorInfo.fragmentClass] to determine which fragment the navigation error
* and [NavigationError.action] originated from. This provides the context for the specific action
* that triggered the navigation. Then, create the appropriate [FragmentTransaction] to manually
* drive the navigation.
*
* In some situations, such as when there are multiple instances of the same fragment, you might
* need an extra context to differentiate the specific instance that generated the [NavigationError],
* if that's the case, use the [NavigationErrorInfo.hashCode] and store it temporarily to compare it
* when another navigation error from the same type of fragment is received. E.g. when handling
* [NavigationError]s from the [HelpFragment] which is used as tutorial/help screen.
*/
private val navigationErrorsObserver = Observer<NavigationError?> { navError ->
navError?.navigationErrorInfo?.let { errorInfo ->
when (errorInfo.fragmentClass) {
HelpFragment::class.java -> {
when (navError.action) {
is NavigationAction.Help.NavigateContinue -> {
if (tutorialFragmentHashCode == errorInfo.hashCode) {
// Handle a continue action from a tutorial screen.
executeFragmentTransaction(BarcodeAnalysisFragment())
} else {
// Handle a continue action from a help screen.
}
}
else -> {}
}
}
BarcodeAnalysisFragment::class.java -> {
when (navError.action) {
is NavigationAction.BarcodeAnalysis.NavigateHelp -> {
executeFragmentTransaction(HelpFragment())
}
else -> {}
}
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ExampleFragmentTransactionBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override fun onStart() {
super.onStart()
/**
* Build a [MiSnapSettings] object and apply it to the [MiSnapWorkflowViewModel] to use it
* during the session. The settings must be applied before moving to the start destination of
* the workflow.
*/
viewModel.applySettings(MiSnapSettings(MiSnapSettings.UseCase.BARCODE, license).apply {
analysis.barcode.trigger = MiSnapSettings.Analysis.Barcode.Trigger.AUTO
})
/**
* Observe the [MiSnapWorkflowViewModel.navigationErrors] [LiveData] for [NavigationError]s and
* handle them accordingly.
*/
viewModel.navigationErrors.observe(this, navigationErrorsObserver)
/**
* Observe the [MiSnapWorkflowViewModel.results] [LiveData] for [MiSnapFinalResult]s and handle
* them accordingly.
*/
viewModel.results.observe(this) { result ->
result?.let {
when (it) {
is MiSnapFinalResult.BarcodeSession -> {
}
else -> {}
}
}
}
/**
* Observe the [MiSnapWorkflowViewModel.error] [LiveData] for [MiSnapErrorResult]s and handle
* them accordingly.
*/
viewModel.error.observe(this) { error ->
error?.let {
when (it.error) {
is MiSnapWorkflowError.Permission -> {
}
is MiSnapWorkflowError.Camera -> {
}
is MiSnapWorkflowError.Cancelled -> {
}
else -> {}
}
}
}
startSession()
}
/**
* Start a transaction to the first destination of the workflow, in this case the [HelpFragment].
*/
private fun startSession() {
val tutorialFragment = HelpFragment()
tutorialFragmentHashCode = tutorialFragment.hashCode()
executeFragmentTransaction(tutorialFragment)
}
/**
* Create and execute [FragmentTransaction]s to drive the navigation manually.
*/
private fun executeFragmentTransaction(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.apply {
replace(binding.fragmentContainer.id, fragment)
}
.commit()
}
}
//sampleEnd
}
Functions
Applies the provided MiSnapSettings to the workflow.
Clears the results object to free up resources used by the returned data. This method is optional, as the data will be released when the Activity associated with the ViewModel is destroyed.
Properties
LiveData for reporting errors that occurred during the session.
A utility to keep track of the most common issues found during a session to use them as diagnostic for failed sessions.
LiveData for reporting navigation errors that occurred during the session.
LiveData for returning results of the session.
The result to be reviewed on the review fragment. The value is automatically set by the provided analysis fragments, and only needs to be set if using the review fragment independently.
LiveData for getting the current settings of the session.