Skip to content

Instantly share code, notes, and snippets.

@hoc081098
Created December 26, 2020 09:03
Show Gist options
  • Select an option

  • Save hoc081098/dfb026224118fac553e48d564e0aa3b0 to your computer and use it in GitHub Desktop.

Select an option

Save hoc081098/dfb026224118fac553e48d564e0aa3b0 to your computer and use it in GitHub Desktop.
ScanQrcodeFragment
package com.doancnpm.edoctor2.ui.main.history
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build
import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.util.Size
import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.doancnpm.edoctor2.R
import com.doancnpm.edoctor2.core.BaseFragment
import com.doancnpm.edoctor2.databinding.FragmentScanQrcodeBinding
import com.doancnpm.edoctor2.domain.dispatchers.AppSchedulers
import com.doancnpm.edoctor2.utils.exhaustMap
import com.doancnpm.edoctor2.utils.toast
import com.doancnpm.edoctor2.utils.viewBinding
import com.google.mlkit.vision.barcode.Barcode
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
import com.jakewharton.rxrelay3.PublishRelay
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.kotlin.addTo
import io.reactivex.rxjava3.kotlin.subscribeBy
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.rx3.rxObservable
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.tasks.await
import org.koin.android.ext.android.inject
import timber.log.Timber
import java.util.concurrent.Executors
import kotlin.coroutines.resume
@ExperimentalCoroutinesApi
class ScanQrcodeFragment : BaseFragment(R.layout.fragment_scan_qrcode) {
private val binding by viewBinding<FragmentScanQrcodeBinding>()
private val navArgs by navArgs<ScanQrcodeFragmentArgs>()
private val schedulers by inject<AppSchedulers>()
private val scanner by lazy { BarcodeScanning.getClient() }
private val imageProxyS = PublishRelay.create<ImageProxy>()
@SuppressLint("UnsafeExperimentalUsageError")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imageProxyS
.exhaustMap { proxy ->
Observable.using(
{ proxy },
{ imageProxy ->
rxObservable {
Timber.d("Analyze: $imageProxy")
val mediaImage = imageProxy.image ?: return@rxObservable
val image = InputImage.fromMediaImage(
mediaImage,
imageProxy.imageInfo.rotationDegrees
)
val qrCode = scanner
.process(image)
.await()
.firstOrNull { it.valueType == Barcode.TYPE_TEXT }
?.displayValue
?: return@rxObservable
send(qrCode)
}
},
ImageProxy::close,
)
}
.take(1)
.observeOn(schedulers.main)
.subscribeBy { code ->
val vibrator = ContextCompat.getSystemService(
requireContext(),
Vibrator::class.java,
)!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(
VibrationEffect.createOneShot(
500,
VibrationEffect.DEFAULT_AMPLITUDE,
)
)
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(500)
}
requireContext().toast("Code: $code")
setFragmentResult(
requestKey = REQUEST_KEY,
result = bundleOf(
QR_CODE_KEY to code,
ORDER_ID_KEY to navArgs.orderId,
)
)
findNavController().popBackStack()
}
.addTo(compositeDisposable)
// Request camera permissions
if (allPermissionsGranted()) {
startCamera()
} else {
viewLifecycleOwner.lifecycleScope.launch {
suspendCancellableCoroutine<Unit> { continuation ->
var launcher: ActivityResultLauncher<Array<String>>? = null
launcher = registerForActivityResult(RequestMultiplePermissions()) {
launcher?.unregister()
continuation.resume(Unit)
}
continuation.invokeOnCancellation { launcher.unregister() }
launcher.launch(REQUIRED_PERMISSIONS)
}
if (allPermissionsGranted()) {
startCamera()
} else {
requireContext().toast("Permissions not granted by the user.")
findNavController().popBackStack()
}
}
}
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener(
Runnable {
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.setTargetResolution(Size(480, 480))
.build()
// Select back camera
val cameraSelector = CameraSelector
.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val imageAnalyzer = ImageAnalysis.Builder()
.setTargetResolution(Size(480, 480))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also { analysis ->
analysis.setAnalyzer(
Executors.newSingleThreadExecutor(),
ImageAnalysis.Analyzer(imageProxyS::accept)
)
}
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this,
cameraSelector,
preview,
imageAnalyzer,
)
preview.setSurfaceProvider(binding.viewFinder.createSurfaceProvider())
} catch (e: Exception) {
Timber.d(e, "Use case binding failed: ${e.message}")
}
},
ContextCompat.getMainExecutor(requireContext()),
)
}
private fun allPermissionsGranted(): Boolean {
return REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
requireContext(),
it
) == PERMISSION_GRANTED
}
}
companion object {
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
val REQUEST_KEY = ScanQrcodeFragment::class.java.name + ".request_key"
val QR_CODE_KEY = ScanQrcodeFragment::class.java.name + ".qr_code_key"
val ORDER_ID_KEY = ScanQrcodeFragment::class.java.name + ".order_id_key"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment