Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions feature/login/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,8 @@ dependencies {

implementation(project(":infra:auth-store"))
implementation(project(":infra:auth-logic"))
implementation(project(":infra:camera"))

// Integrity check related
implementation(libs.playServices.base)
Comment thread
luhmirin-s marked this conversation as resolved.

// QR scanner related
implementation(libs.androidX.cameraX.core)
implementation(libs.androidX.cameraX.lifecycle)
implementation(libs.androidX.cameraX.view)
implementation(libs.playServices.barcode)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,35 @@ import androidx.navigation.fragment.findNavController
import com.simprints.core.tools.extensions.hasPermission
import com.simprints.feature.login.R
import com.simprints.feature.login.databinding.FragmentQrScannerBinding
import com.simprints.infra.camera.CameraFrameProvider
import com.simprints.infra.camera.postprocess.DetectQrCodeUseCase
import com.simprints.infra.camera.usecase.GetBoundsRelativeToParentUseCase
import com.simprints.infra.logging.LoggingConstants
import com.simprints.infra.uibase.camera.qrscan.CameraHelper
import com.simprints.infra.uibase.camera.qrscan.QrCodeAnalyzer
import com.simprints.infra.logging.Simber
import com.simprints.infra.uibase.view.applySystemBarInsets
import com.simprints.infra.uibase.navigation.finishWithResult
import com.simprints.infra.uibase.view.applySystemBarInsets
import com.simprints.infra.uibase.view.awaitLayout
import com.simprints.infra.uibase.viewbinding.viewBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
Comment thread
luhmirin-s marked this conversation as resolved.
import javax.inject.Inject

@AndroidEntryPoint
internal class QrScannerFragment : Fragment(R.layout.fragment_qr_scanner) {
private val binding by viewBinding(FragmentQrScannerBinding::bind)
private val crashReportTag = LoggingConstants.CrashReportTag.LOGIN

@Inject
lateinit var cameraHelperFactory: CameraHelper.Factory
lateinit var cameraFrameProvider: CameraFrameProvider

@Inject
lateinit var qrCodeAnalyzerFactory: QrCodeAnalyzer.Factory
lateinit var getBoundsRelativeToParentUseCase: GetBoundsRelativeToParentUseCase

private val cameraHelper: CameraHelper by lazy {
cameraHelperFactory.create(crashReportTag)
}
private val qrCodeAnalyzer by lazy {
qrCodeAnalyzerFactory.create(cropConfig = null, crashReportTag = crashReportTag)
@Inject
lateinit var detectQrCodeUseCaseFactory: DetectQrCodeUseCase.Factory
private val detectQrCodeUseCase: DetectQrCodeUseCase by lazy {
detectQrCodeUseCaseFactory.create(crashReportTag = crashReportTag)
}

private val launchPermissionRequest = registerForActivityResult(
Expand All @@ -59,36 +61,48 @@ internal class QrScannerFragment : Fragment(R.layout.fragment_qr_scanner) {
super.onViewCreated(view, savedInstanceState)
applySystemBarInsets(view)

viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
qrCodeAnalyzer.scannedCode
.catch { e ->
Simber.e("Camera not available for QR scanning", e, tag = crashReportTag)
finishWithError(QrScannerResult.QrScannerError.CameraNotAvailable)
}.collectLatest { qrCode ->
if (qrCode.isNotEmpty()) {
finishWithContent(qrCode)
}
}
}
}

startAnalyzer()
if (requireActivity().hasPermission(CAMERA)) {
startCamera()
} else {
launchPermissionRequest.launch(CAMERA)
}
}

private fun startCamera() {
binding.qrScannerArea.isVisible = true
cameraHelper.startCamera(
viewLifecycleOwner,
binding.qrScannerPreview,
qrCodeAnalyzer,
override fun onDestroyView() {
cameraFrameProvider.release()
super.onDestroyView()
}

private fun startAnalyzer() = viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
cameraFrameProvider.frames
.catch { e ->
Simber.e("Camera not available for QR scanning", e, tag = crashReportTag)
finishWithError(QrScannerResult.QrScannerError.CameraNotAvailable)
}.collect { frame ->
detectQrCodeUseCase(frame)?.takeIf { it.isNotEmpty() }?.let { qrCode -> finishWithContent(qrCode) }
}
}
}

private fun startCamera() = viewLifecycleOwner.lifecycleScope.launch {
// Wait for the views to be properly laid out
binding.qrScannerPreview.awaitLayout()
binding.qrScannerArea.awaitLayout()

Comment thread
luhmirin-s marked this conversation as resolved.
val targetRect = getBoundsRelativeToParentUseCase(
parent = binding.qrScannerPreview,
child = binding.qrScannerArea,
)
cameraFrameProvider.initialiseCamera(
lifecycleOwner = viewLifecycleOwner,
previewView = binding.qrScannerPreview,
target = targetRect,
) {
finishWithError(QrScannerResult.QrScannerError.CameraNotAvailable)
}
binding.qrScannerArea.isVisible = true
}

private fun finishWithContent(content: String) {
Expand Down
2 changes: 1 addition & 1 deletion feature/login/src/main/res/layout/fragment_qr_scanner.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_camera_focus"
android:visibility="gone"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
Expand Down