Search code examples
androidmvvmzxingback-buttonback

back button to escape Zxing scanner in MVVM


I've been trying to use zxing library, but I can't get it to work properly.

I created a layout xml, a viewmodel and a fragment because my project is MVVM pattern. Originally, it shows my layout view. But The problem is Zxing has its own screen in the library that it scans the code with it. So what's happening is when back button is pressed, I can not go back but it dismisses its own screen and then shows my layout. As a result, I have to press back button twice to escape zxing scanner and my layout view.

Any help will be greatly appreciated.

What I have tried

  1. I set onBackPressed to back two stacks. But it still shows my layout after zxing scanner.
  2. I tried to call the scanner on my layout xml but it was unsuccessful with the same reason.

What I want to do Press back button once to escape qr scanner screen.

fragment

class QrScanFragment  : Fragment() {

private lateinit var binding: FragmentQrScanBinding
private lateinit var viewModel: QrScanViewModel
private lateinit var mContext: MainActivity
private lateinit var callback : OnBackPressedCallback

override fun onAttach(context: Context) {
    super.onAttach(context)
    mContext = context as MainActivity
    callback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Timber.d("onBackPressed")
            findNavController().popBackStack()
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
    binding = DataBindingUtil.inflate(inflater, R.layout.fragment_qr_scan, container, false)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewModel = ViewModelProvider(this).get(QrScanViewModel::class.java)
    binding.viewModel = viewModel
    binding.lifecycleOwner = viewLifecycleOwner

    val integrator = IntentIntegrator.forSupportFragment(this@QrScanFragment)
    integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
    integrator.setPrompt("Scan code")
    integrator.setCameraId(0)
    integrator.setBeepEnabled(false)
    integrator.setBarcodeImageEnabled(true)
    integrator.initiateScan()

}

override fun onDetach() {
    super.onDetach()
    callback.remove()
}
}

xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
    <variable
        name="viewModel"
        type="com.project.view.qrScan.QrScanViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

viewModel

class QrScanViewModel: ViewModel(){}

Solution

  • Use onActivityResult for getting callback. From this callback, you could dismiss your view

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
        if(result != null) {
            if(result.getContents() == null) {
                //Dismiss your view if Zxing was cancelled
            } else {
                //Zxing returned with result, you could should the result or dismiss your view
            }
        } else {
            // This is important, otherwise the result will not be passed to the fragment
            super.onActivityResult(requestCode, resultCode, data);
        }
    }