From f9e9dcf7bcc882986fb3466f9d1b474fe82569f8 Mon Sep 17 00:00:00 2001 From: alexandr Date: Wed, 29 May 2024 16:06:45 +0300 Subject: [PATCH 1/4] [MS-226] LiveFeedbackFragment now utilizes different layouts for horizontal and vertical screen orientations. Due to historical reasons, the preview is formed around the guideline in the layout, whose height is defined as percentage of the screen height. It is hardcoded to 0.3 (30% of sceren height), and it doesn't work for the smaller horizontal dimensions. Thus, the new horizontal layout is introduced, that has a guideline set at the 0.5 (50% of screen height). --- .../face/capture/models/ScreenOrientation.kt | 16 ++++ .../screens/livefeedback/FrameProcessor.kt | 27 ++++-- .../livefeedback/LiveFeedbackFragment.kt | 21 ++++- .../LiveFeedbackFragmentViewModel.kt | 7 +- .../livefeedback/views/CameraTargetOverlay.kt | 44 +++++++-- .../layout-land/fragment_live_feedback.xml | 91 +++++++++++++++++++ .../res/layout/fragment_live_feedback.xml | 4 + 7 files changed, 185 insertions(+), 25 deletions(-) create mode 100644 face/capture/src/main/java/com/simprints/face/capture/models/ScreenOrientation.kt create mode 100644 face/capture/src/main/res/layout-land/fragment_live_feedback.xml diff --git a/face/capture/src/main/java/com/simprints/face/capture/models/ScreenOrientation.kt b/face/capture/src/main/java/com/simprints/face/capture/models/ScreenOrientation.kt new file mode 100644 index 0000000000..c1a804bdd0 --- /dev/null +++ b/face/capture/src/main/java/com/simprints/face/capture/models/ScreenOrientation.kt @@ -0,0 +1,16 @@ +package com.simprints.face.capture.models + +import android.content.res.Configuration +import android.content.res.Resources + +enum class ScreenOrientation { + Landscape, Portrait; + + companion object { + fun getCurrentOrientation(resources: Resources) = + when (resources.configuration.orientation) { + Configuration.ORIENTATION_LANDSCAPE -> Landscape + else -> Portrait + } + } +} \ No newline at end of file diff --git a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt index 2479252786..cab1f40a1b 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt @@ -6,6 +6,7 @@ import android.graphics.RectF import android.util.Size import androidx.camera.core.ImageProxy import androidx.core.graphics.toRect +import com.simprints.face.capture.models.ScreenOrientation import com.simprints.face.capture.screens.livefeedback.views.CameraTargetOverlay import com.simprints.face.capture.usecases.ImageProxyToBitmapUseCase import java.lang.Float.min @@ -20,7 +21,7 @@ internal class FrameProcessor @Inject constructor( private var previewViewHeight: Int = 0 private lateinit var boxOnTheScreen: RectF - private lateinit var cropRect: Rect + private var cropRect: Rect? = null /** * Init the frame processor @@ -35,6 +36,10 @@ internal class FrameProcessor @Inject constructor( this.boxOnTheScreen = boxOnTheScreen } + fun clear() { + cropRect = null + } + /** * Extracts part of the image that lays inside * the cropRect @@ -42,15 +47,13 @@ internal class FrameProcessor @Inject constructor( * @param image * @return Bitmap */ - fun cropRotateFrame(image: ImageProxy): Bitmap { - if (!this::cropRect.isInitialized) { - // The cropRect should be calculated once as its value will be the same for all images. - calcRotatedCropRect(image) - } + fun cropRotateFrame(image: ImageProxy, screenOrientation: ScreenOrientation): Bitmap { + val cropRect = this.cropRect + ?: calcRotatedCropRect(image, screenOrientation).also { this.cropRect = it } return imageProxyToBitmap(image, cropRect) } - private fun calcRotatedCropRect(image: ImageProxy) { + private fun calcRotatedCropRect(image: ImageProxy, screenOrientation: ScreenOrientation): Rect { val cameraWidth = image.width val cameraHeight = image.height @@ -64,10 +67,14 @@ internal class FrameProcessor @Inject constructor( boxOnTheScreen ) - val newBoundingBox = - CameraTargetOverlay.rectForPlane(rotatedCameraWidth, rotatedCameraHeight, newRectSize) + val newBoundingBox = CameraTargetOverlay.rectForPlane( + width = rotatedCameraWidth, + height = rotatedCameraHeight, + rectSize = newRectSize, + screenOrientation = screenOrientation + ) - cropRect = getRotatedBoundingBox( + return getRotatedBoundingBox( image.imageInfo.rotationDegrees, newBoundingBox, cameraWidth, diff --git a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragment.kt b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragment.kt index 03b8a1bfea..9319577e66 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragment.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragment.kt @@ -24,6 +24,7 @@ import com.simprints.core.tools.extentions.hasPermission import com.simprints.face.capture.R import com.simprints.face.capture.databinding.FragmentLiveFeedbackBinding import com.simprints.face.capture.models.FaceDetection +import com.simprints.face.capture.models.ScreenOrientation import com.simprints.face.capture.screens.FaceCaptureViewModel import com.simprints.infra.logging.Simber import com.simprints.infra.uibase.navigation.navigateSafely @@ -76,6 +77,11 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback) initFragment() } + override fun onDestroyView() { + vm.clearFrameProcessor() + super.onDestroyView() + } + private fun initFragment() { screenSize = with(resources.displayMetrics) { Size(widthPixels, widthPixels) } @@ -133,7 +139,7 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback) override fun onStop() { // Shut down our background executor - if(::cameraExecutor.isInitialized) { + if (::cameraExecutor.isInitialized) { cameraExecutor.shutdown() } super.onStop() @@ -161,7 +167,10 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback) private fun analyze(image: ImageProxy) { try { - vm.process(image) + vm.process( + image = image, + screenOrientation = ScreenOrientation.getCurrentOrientation(resources) + ) } catch (t: Throwable) { Simber.e(t) // Image analysis is running in bg thread @@ -185,7 +194,9 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback) private fun renderCapturingStateColors() { with(binding) { - captureOverlay.drawWhiteTarget() + captureOverlay.drawWhiteTarget( + screenOrientation = ScreenOrientation.getCurrentOrientation(resources) + ) captureTitle.setTextColor( ContextCompat.getColor(requireContext(), IDR.color.simprints_blue_grey) @@ -198,7 +209,9 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback) private fun renderCapturingNotStarted() { binding.apply { - captureOverlay.drawSemiTransparentTarget() + captureOverlay.drawSemiTransparentTarget( + screenOrientation = ScreenOrientation.getCurrentOrientation(resources) + ) captureTitle.text = getString(IDR.string.face_capture_preparation_title) captureFeedbackTxtTitle.text = getString(IDR.string.face_capture_title_previewing) } diff --git a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModel.kt b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModel.kt index e946ede860..853e2f7786 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModel.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModel.kt @@ -11,6 +11,7 @@ import com.simprints.core.tools.extentions.area import com.simprints.core.tools.time.TimeHelper import com.simprints.face.capture.models.FaceDetection import com.simprints.face.capture.models.FaceTarget +import com.simprints.face.capture.models.ScreenOrientation import com.simprints.face.capture.models.SymmetricTarget import com.simprints.face.capture.usecases.SimpleCaptureEventReporter import com.simprints.infra.config.store.ConfigRepository @@ -55,9 +56,9 @@ internal class LiveFeedbackFragmentViewModel @Inject constructor( * * @param image is the camera frame */ - fun process(image: ImageProxy) { + fun process(image: ImageProxy, screenOrientation: ScreenOrientation) { val captureStartTime = timeHelper.now() - val croppedBitmap = frameProcessor.cropRotateFrame(image) + val croppedBitmap = frameProcessor.cropRotateFrame(image, screenOrientation) val potentialFace = faceDetector.analyze(croppedBitmap) val faceDetection = getFaceDetectionFromPotentialFace(croppedBitmap, potentialFace) @@ -92,6 +93,8 @@ internal class LiveFeedbackFragmentViewModel @Inject constructor( frameProcessor.init(previewSize, cropRect) } + fun clearFrameProcessor() = frameProcessor.clear() + fun startCapture() { capturingState.value = CapturingState.CAPTURING } diff --git a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/views/CameraTargetOverlay.kt b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/views/CameraTargetOverlay.kt index f4669f8c1c..65b772bfce 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/views/CameraTargetOverlay.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/views/CameraTargetOverlay.kt @@ -1,11 +1,17 @@ package com.simprints.face.capture.screens.livefeedback.views import android.content.Context -import android.graphics.* +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode +import android.graphics.RectF import android.util.AttributeSet import android.view.View import androidx.appcompat.widget.AppCompatImageView import com.simprints.core.tools.extentions.dpToPx +import com.simprints.face.capture.models.ScreenOrientation import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageReports @ExcludedFromGeneratedTestCoverageReports("UI code") @@ -16,9 +22,29 @@ internal class CameraTargetOverlay( companion object { private val SEMI_TRANSPARENT_OVERLAY = Color.argb(102, 0, 0, 0) private val WHITE_OVERLAY = Color.argb(242, 255, 255, 255) - private const val percentFromTop = 0.3f - fun rectForPlane(width: Int, height: Int, rectSize: Float): RectF { + /** + * Reference to the guideline's percentage of margin from the top of the screen. Used when + * the screen is in the portrait (vertical) mode + */ + private const val percentFromTopPortrait = 0.3f + + /** + * Reference to the guideline's percentage of margin from the top of the screen. Used when + * the screen is in the landscape (horizontal) mode + */ + private const val percentFromTopLandscape = 0.5f + + fun rectForPlane( + width: Int, + height: Int, + rectSize: Float, + screenOrientation: ScreenOrientation + ): RectF { + val percentFromTop = when (screenOrientation) { + ScreenOrientation.Landscape -> percentFromTopLandscape + ScreenOrientation.Portrait -> percentFromTopPortrait + } val top = (height * percentFromTop) - (rectSize / 2) val bottom = top + rectSize @@ -59,24 +85,24 @@ internal class CameraTargetOverlay( drawingFunc?.invoke(canvas) } - fun drawSemiTransparentTarget() { + fun drawSemiTransparentTarget(screenOrientation: ScreenOrientation) { drawingFunc = { drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) drawColor(SEMI_TRANSPARENT_OVERLAY, PorterDuff.Mode.SRC_OVER) - drawTarget() + drawTarget(screenOrientation) } } - fun drawWhiteTarget() { + fun drawWhiteTarget(screenOrientation: ScreenOrientation) { drawingFunc = { drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) drawColor(WHITE_OVERLAY, PorterDuff.Mode.SRC_OVER) - drawTarget() + drawTarget(screenOrientation) } } - private fun Canvas.drawTarget() { - rectInCanvas = rectForPlane(width, height, rectSize) + private fun Canvas.drawTarget(screenOrientation: ScreenOrientation) { + rectInCanvas = rectForPlane(width, height, rectSize, screenOrientation) drawOval(rectInCanvas, circlePaint) drawOval(rectInCanvas, circleBorderPaint) diff --git a/face/capture/src/main/res/layout-land/fragment_live_feedback.xml b/face/capture/src/main/res/layout-land/fragment_live_feedback.xml new file mode 100644 index 0000000000..17ea5fdb77 --- /dev/null +++ b/face/capture/src/main/res/layout-land/fragment_live_feedback.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/face/capture/src/main/res/layout/fragment_live_feedback.xml b/face/capture/src/main/res/layout/fragment_live_feedback.xml index 0e708ddd71..2091b1e5b0 100644 --- a/face/capture/src/main/res/layout/fragment_live_feedback.xml +++ b/face/capture/src/main/res/layout/fragment_live_feedback.xml @@ -15,6 +15,10 @@ android:layout_height="match_parent" android:keepScreenOn="true" /> + Date: Thu, 30 May 2024 11:35:19 +0300 Subject: [PATCH 2/4] [MS-226] Fixing tests --- .../livefeedback/FrameProcessorTest.kt | 15 ++++---- .../LiveFeedbackFragmentViewModelTest.kt | 37 +++++++++++-------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt b/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt index faf984bfdd..93e0dd606a 100644 --- a/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt +++ b/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt @@ -6,6 +6,7 @@ import android.graphics.RectF import android.util.Size import androidx.camera.core.ImageProxy import com.google.common.truth.Truth.assertThat +import com.simprints.face.capture.models.ScreenOrientation import com.simprints.face.capture.usecases.ImageProxyToBitmapUseCase import io.mockk.CapturingSlot import io.mockk.MockKAnnotations @@ -52,7 +53,7 @@ internal class FrameProcessorTest { frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) // When - frameProcessor.cropRotateFrame(image) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) // Then assertThat(cropRectCapture.captured.toString()) @@ -71,7 +72,7 @@ internal class FrameProcessorTest { frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) // When - frameProcessor.cropRotateFrame(image) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) // Then assertThat(cropRectCapture.captured.toString()) @@ -90,7 +91,7 @@ internal class FrameProcessorTest { frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) // When - frameProcessor.cropRotateFrame(image) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) // Then assertThat(cropRectCapture.captured.toString()) @@ -109,7 +110,7 @@ internal class FrameProcessorTest { frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) // When - frameProcessor.cropRotateFrame(image) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) // Then assertThat(cropRectCapture.captured.toString()) @@ -128,7 +129,7 @@ internal class FrameProcessorTest { frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) // When - frameProcessor.cropRotateFrame(image) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) // Then throw IllegalArgumentException } @@ -145,7 +146,7 @@ internal class FrameProcessorTest { frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) // When - frameProcessor.cropRotateFrame(image) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) // Then assertThat(cropRectCapture.captured.toString()) .isEqualTo(Rect(100, 100, 200, 200).toString()) @@ -163,7 +164,7 @@ internal class FrameProcessorTest { frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) // When - frameProcessor.cropRotateFrame(image) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) // Then assertThat(cropRectCapture.captured.toString()) .isEqualTo(Rect(100, 100, 200, 200).toString()) diff --git a/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModelTest.kt b/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModelTest.kt index 335f6eae49..41dae7f79b 100644 --- a/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModelTest.kt +++ b/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModelTest.kt @@ -10,21 +10,25 @@ import com.google.common.truth.Truth.assertThat import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.face.capture.models.FaceDetection +import com.simprints.face.capture.models.ScreenOrientation import com.simprints.face.capture.usecases.SimpleCaptureEventReporter import com.simprints.infra.config.store.ConfigRepository import com.simprints.infra.facebiosdk.detection.Face import com.simprints.infra.facebiosdk.detection.FaceDetector import com.simprints.testtools.common.coroutines.TestCoroutineRule import com.simprints.testtools.common.livedata.testObserver -import io.mockk.* +import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every import io.mockk.impl.annotations.MockK +import io.mockk.justRun import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import java.util.* import kotlin.random.Random @RunWith(RobolectricTestRunner::class) @@ -61,6 +65,7 @@ internal class LiveFeedbackFragmentViewModelTest { lateinit var timeHelper: TimeHelper private val previewViewSize: Size = Size(100, 100) + private val screenOrientation = ScreenOrientation.Portrait private lateinit var viewModel: LiveFeedbackFragmentViewModel @@ -73,7 +78,7 @@ internal class LiveFeedbackFragmentViewModelTest { justRun { frameProcessor.init(any(), any()) } justRun { frame.close() } justRun { previewFrame.recycle() } - every { frameProcessor.cropRotateFrame(frame) } returns previewFrame + every { frameProcessor.cropRotateFrame(frame, screenOrientation) } returns previewFrame viewModel = LiveFeedbackFragmentViewModel( frameProcessor, @@ -89,7 +94,7 @@ internal class LiveFeedbackFragmentViewModelTest { coEvery { faceDetector.analyze(previewFrame) } returns getFace() viewModel.initFrameProcessor(1, 0, rectF, previewViewSize) - viewModel.process(frame) + viewModel.process(frame, screenOrientation) val currentDetection = viewModel.currentDetection.testObserver() assertThat(currentDetection.observedValues.last()?.status).isEqualTo(FaceDetection.Status.VALID) @@ -102,9 +107,9 @@ internal class LiveFeedbackFragmentViewModelTest { coEvery { faceDetector.analyze(previewFrame) } returns getFace() viewModel.initFrameProcessor(1, 0, rectF, previewViewSize) - viewModel.process(frame) + viewModel.process(frame, screenOrientation) viewModel.startCapture() - viewModel.process(frame) + viewModel.process(frame, screenOrientation) val currentDetection = viewModel.currentDetection.testObserver() assertThat(currentDetection.observedValues.last()?.status).isEqualTo(FaceDetection.Status.VALID_CAPTURING) @@ -120,7 +125,7 @@ internal class LiveFeedbackFragmentViewModelTest { val rolledFace: Face = getFace(roll = 45f) val noFace = null - every { frameProcessor.cropRotateFrame(frame) } returns previewFrame + every { frameProcessor.cropRotateFrame(frame, screenOrientation) } returns previewFrame every { faceDetector.analyze(previewFrame) } returnsMany listOf( smallFace, bigFace, @@ -132,11 +137,11 @@ internal class LiveFeedbackFragmentViewModelTest { val detections = viewModel.currentDetection.testObserver() viewModel.initFrameProcessor(2, 0, rectF, previewViewSize) - viewModel.process(frame) - viewModel.process(frame) - viewModel.process(frame) - viewModel.process(frame) - viewModel.process(frame) + viewModel.process(frame, screenOrientation) + viewModel.process(frame, screenOrientation) + viewModel.process(frame, screenOrientation) + viewModel.process(frame, screenOrientation) + viewModel.process(frame, screenOrientation) detections.observedValues.let { assertThat(it[0]?.status).isEqualTo(FaceDetection.Status.TOOFAR) @@ -152,17 +157,17 @@ internal class LiveFeedbackFragmentViewModelTest { @Test fun `Save all valid captures without fallback image`() = runTest { val validFace: Face = getFace() - every { frameProcessor.cropRotateFrame(frame) } returns previewFrame + every { frameProcessor.cropRotateFrame(frame, screenOrientation) } returns previewFrame every { faceDetector.analyze(previewFrame) } returns validFace every { timeHelper.now() } returnsMany (0..100L).map { Timestamp(it) } val currentDetectionObserver = viewModel.currentDetection.testObserver() val capturingStateObserver = viewModel.capturingState.testObserver() viewModel.initFrameProcessor(2, 0, rectF, previewViewSize) - viewModel.process(frame) + viewModel.process(frame, screenOrientation) viewModel.startCapture() - viewModel.process(frame) - viewModel.process(frame) + viewModel.process(frame, screenOrientation) + viewModel.process(frame, screenOrientation) currentDetectionObserver.observedValues.let { assertThat(it[0]?.status).isEqualTo(FaceDetection.Status.VALID) From 90e13e2a103ac7c28365cd1185fcbcea4f37ca0f Mon Sep 17 00:00:00 2001 From: alexandr Date: Thu, 30 May 2024 16:07:14 +0300 Subject: [PATCH 3/4] [MS-483] Adding test coverage for the ScreenOrientation class --- .../capture/models/ScreenOrientationTest.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 face/capture/src/test/java/com/simprints/face/capture/models/ScreenOrientationTest.kt diff --git a/face/capture/src/test/java/com/simprints/face/capture/models/ScreenOrientationTest.kt b/face/capture/src/test/java/com/simprints/face/capture/models/ScreenOrientationTest.kt new file mode 100644 index 0000000000..55a332873a --- /dev/null +++ b/face/capture/src/test/java/com/simprints/face/capture/models/ScreenOrientationTest.kt @@ -0,0 +1,42 @@ +package com.simprints.face.capture.models + +import android.content.res.Configuration +import android.content.res.Resources +import com.google.common.truth.Truth.assertThat +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import org.junit.Before +import org.junit.Test + + +class ScreenOrientationTest { + + @MockK + lateinit var resources: Resources + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + } + + @Test + fun `when the resources are provided, then the correct screen orientation is mapped`() { + val orientationMap = mapOf( + Configuration.ORIENTATION_LANDSCAPE to ScreenOrientation.Landscape, + Configuration.ORIENTATION_PORTRAIT to ScreenOrientation.Portrait + ) + orientationMap.forEach {entry -> + val resourceOrientation = entry.key + val expectedOrientation = entry.value + + every { resources.configuration } returns mockk { + orientation = resourceOrientation + } + val orientation = ScreenOrientation.getCurrentOrientation(resources) + assertThat(orientation).isEqualTo(expectedOrientation) + } + } + +} \ No newline at end of file From 7e3bb9935231804a1fe5dd9addbfbb1c82ec2044 Mon Sep 17 00:00:00 2001 From: alexandr Date: Thu, 30 May 2024 16:23:33 +0300 Subject: [PATCH 4/4] [MS-483] Adding test coverage for the FrameProcessor class. Adding public getter for the 'cropRect' property for testing purposes --- .../screens/livefeedback/FrameProcessor.kt | 3 ++- .../livefeedback/FrameProcessorTest.kt | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt index cab1f40a1b..345805d32e 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/FrameProcessor.kt @@ -21,7 +21,8 @@ internal class FrameProcessor @Inject constructor( private var previewViewHeight: Int = 0 private lateinit var boxOnTheScreen: RectF - private var cropRect: Rect? = null + var cropRect: Rect? = null + private set /** * Init the frame processor diff --git a/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt b/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt index 93e0dd606a..eeab24831a 100644 --- a/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt +++ b/face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/FrameProcessorTest.kt @@ -170,4 +170,23 @@ internal class FrameProcessorTest { .isEqualTo(Rect(100, 100, 200, 200).toString()) } + @Test + fun `when clear is called, cropRect becomes null`() { + every { image.width } returns 2000 + every { image.height } returns 1000 + every { image.imageInfo.rotationDegrees } returns 90 + + val screenWidth = 1000 + val screenHeight = 500 + + frameProcessor.init(Size(screenWidth, screenHeight), boxOnTheScreen) + frameProcessor.cropRotateFrame(image, ScreenOrientation.Portrait) + + assertThat(frameProcessor.cropRect).isNotNull() + + frameProcessor.clear() + + assertThat(frameProcessor.cropRect).isNull() + } + }