Skip to content

Commit

Permalink
feat: QrCodeImport migrated to MVVM
Browse files Browse the repository at this point in the history
  • Loading branch information
PratyushSingh07 committed Jul 24, 2023
1 parent c2e3ed4 commit 74ce257
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.google.zxing.Result
Expand All @@ -17,34 +18,32 @@ import dagger.hilt.android.AndroidEntryPoint
import org.mifos.mobile.R
import org.mifos.mobile.databinding.FragmentQrCodeImportBinding
import org.mifos.mobile.models.beneficiary.Beneficiary
import org.mifos.mobile.presenters.QrCodeImportPresenter
import org.mifos.mobile.ui.activities.base.BaseActivity
import org.mifos.mobile.ui.enums.BeneficiaryState
import org.mifos.mobile.ui.fragments.base.BaseFragment
import org.mifos.mobile.ui.views.QrCodeImportView
import org.mifos.mobile.utils.Constants
import org.mifos.mobile.utils.QrCodeUiState
import org.mifos.mobile.utils.Toaster
import org.mifos.mobile.viewModels.QrCodeImportViewModel
import java.io.FileNotFoundException
import java.io.InputStream
import javax.inject.Inject

/**
* Created by manishkumar on 19/05/18.
*/
@AndroidEntryPoint
class QrCodeImportFragment : BaseFragment(), QrCodeImportView {
class QrCodeImportFragment : BaseFragment() {

private var _binding: FragmentQrCodeImportBinding? = null
private val binding get() = _binding!!

private lateinit var viewModel: QrCodeImportViewModel

private lateinit var qrUri: Uri
private var uriValue: String? = null
private var mFrameRect: RectF? = null
private var inputStream: InputStream? = null

@JvmField
@Inject
var qrCodeImportPresenter: QrCodeImportPresenter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null) {
Expand All @@ -60,22 +59,40 @@ class QrCodeImportFragment : BaseFragment(), QrCodeImportView {
savedInstanceState: Bundle?,
): View {
_binding = FragmentQrCodeImportBinding.inflate(inflater, container, false)
viewModel = ViewModelProvider(this)[QrCodeImportViewModel::class.java]
setToolbarTitle(getString(R.string.import_qr))
// load the uri
setBitmapImage(qrUri)
binding.ivCropQrCode.setCompressFormat(Bitmap.CompressFormat.JPEG)
binding.ivCropQrCode.setOutputMaxSize(150, 150)
binding.ivCropQrCode.load(qrUri)
?.initialFrameRect(mFrameRect)
?.executeAsCompletable()
binding.ivCropQrCode.setCropMode(CropImageView.CropMode.FREE)
binding.ivCropQrCode.setInitialFrameScale(0.8f)
qrCodeImportPresenter?.attachView(this)
with(binding) {
ivCropQrCode.setCompressFormat(Bitmap.CompressFormat.JPEG)
ivCropQrCode.setOutputMaxSize(150, 150)
ivCropQrCode.load(qrUri)
?.initialFrameRect(mFrameRect)
?.executeAsCompletable()
ivCropQrCode.setCropMode(CropImageView.CropMode.FREE)
ivCropQrCode.setInitialFrameScale(0.8f)
}

return binding.root
}

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

viewModel.qrCodeUiState.observe(viewLifecycleOwner) {
when (it) {
is QrCodeUiState.Loading -> showProgress()
is QrCodeUiState.ShowError -> {
hideProgress()
showErrorReadingQr(getString(it.message))
}
is QrCodeUiState.HandleDecodedResult -> {
hideProgress()
handleDecodedResult(it.result)
}
}
}

binding.btnProceed.setOnClickListener {
proceed()
}
Expand All @@ -98,15 +115,15 @@ class QrCodeImportFragment : BaseFragment(), QrCodeImportView {
}

fun proceed() {
qrCodeImportPresenter?.getDecodedResult(qrUri, binding.ivCropQrCode)
viewModel.getDecodedResult(qrUri, binding.ivCropQrCode)
}

/**
* It is called whenever any error occurs while executing a request
*
* @param message Error message that tells the user about the problem.
*/
override fun showErrorReadingQr(message: String?) {
fun showErrorReadingQr(message: String?) {
Toaster.show(binding.root, message)
}

Expand All @@ -117,7 +134,7 @@ class QrCodeImportFragment : BaseFragment(), QrCodeImportView {
*
* @param result contains the results from decoded QR bitmap
*/
override fun handleDecodedResult(result: Result?) {
fun handleDecodedResult(result: Result?) {
val gson = Gson()
try {
val beneficiary = gson.fromJson(result?.text, Beneficiary::class.java)
Expand All @@ -139,21 +156,20 @@ class QrCodeImportFragment : BaseFragment(), QrCodeImportView {
/**
* Shows [org.mifos.mobile.utils.ProgressBarHandler]
*/
override fun showProgress() {
fun showProgress() {
showProgressBar()
}

/**
* Hides [org.mifos.mobile.utils.ProgressBarHandler]
*/
override fun hideProgress() {
fun hideProgress() {
hideProgressBar()
}

override fun onDestroyView() {
super.onDestroyView()
hideProgress()
qrCodeImportPresenter?.detachView()
_binding = null
}

Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/org/mifos/mobile/utils/QrCodeUiState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.mifos.mobile.utils

import com.google.zxing.Result

sealed class QrCodeUiState {
object Loading : QrCodeUiState()
data class ShowError(val message: Int) : QrCodeUiState()
data class HandleDecodedResult(val result: Result?) : QrCodeUiState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.mifos.mobile.viewModels

import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.google.zxing.*
import com.google.zxing.common.HybridBinarizer
import com.isseiaoki.simplecropview.CropImageView
import dagger.hilt.android.lifecycle.HiltViewModel
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import org.mifos.mobile.R
import org.mifos.mobile.utils.QrCodeUiState
import java.util.*
import javax.inject.Inject

@HiltViewModel
class QrCodeImportViewModel @Inject constructor() : ViewModel() {

private val compositeDisposables: CompositeDisposable = CompositeDisposable()

private var result: Result? = null
private var hasErrorOccurred = false

private val _qrCodeUiState = MutableLiveData<QrCodeUiState>()
val qrCodeUiState: LiveData<QrCodeUiState> = _qrCodeUiState

fun getDecodedResult(sourceUri: Uri?, cropImageView: CropImageView?) {
_qrCodeUiState.value = QrCodeUiState.Loading
cropImageView?.crop(sourceUri)
?.executeAsSingle()
?.flatMap { bMap ->
val intArray = IntArray(bMap.width * bMap.height)
// copy pixel data from the Bitmap into the 'intArray' array
bMap.getPixels(
intArray,
0,
bMap.width,
0,
0,
bMap.width,
bMap.height,
)
val source: LuminanceSource = RGBLuminanceSource(
bMap.width,
bMap.height,
intArray,
)
val bitmap = BinaryBitmap(HybridBinarizer(source))
// flags for decoding since it is large file
val tmpHintsMap: MutableMap<DecodeHintType, Boolean?> =
EnumMap(DecodeHintType::class.java)
tmpHintsMap[DecodeHintType.TRY_HARDER] = java.lang.Boolean.TRUE
tmpHintsMap[DecodeHintType.PURE_BARCODE] = java.lang.Boolean.TRUE
val reader: Reader = MultiFormatReader()
try {
result = reader.decode(bitmap, tmpHintsMap)
} catch (e: Exception) {
_qrCodeUiState.value = QrCodeUiState.ShowError(R.string.error_reading_qr)
}
Single.just(result)
}
?.subscribeOn(Schedulers.newThread())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe({ result ->
_qrCodeUiState.value = QrCodeUiState.HandleDecodedResult(result)
}) {
if (!hasErrorOccurred) {
_qrCodeUiState.value = QrCodeUiState.ShowError(R.string.error_reading_qr)
}
hasErrorOccurred = false
}?.let { compositeDisposables.add(it) }
}
}

0 comments on commit 74ce257

Please sign in to comment.