diff --git a/CHANGELOG.md b/CHANGELOG.md index df3019d..ec3bcf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ The changes documented here do not include those from the original repository. ## [Unreleased] +### 13-12-2023 +Android - Refactor - Use coroutines to work with bitmaps (https://outsystemsrd.atlassian.net/browse/RMET-2912) + ### 06-12-2023 Android - Implement scanning only the frame area (Portrait, Landscape, Adaptive) (https://outsystemsrd.atlassian.net/browse/RMET-2912) diff --git a/pom.xml b/pom.xml index af24ad8..5ab9da9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,5 +7,5 @@ 4.0.0 com.github.outsystems osbarcode-android - 0.0.29 + 0.0.30 diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCBarcodeAnalyzer.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCBarcodeAnalyzer.kt index 19f767b..0a1b7bf 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCBarcodeAnalyzer.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCBarcodeAnalyzer.kt @@ -11,6 +11,9 @@ import com.outsystems.plugins.barcode.controller.helper.OSBARCImageHelperInterfa import com.outsystems.plugins.barcode.model.OSBARCError import com.outsystems.plugins.barcode.view.ui.theme.SizeRatioHeight import com.outsystems.plugins.barcode.view.ui.theme.SizeRatioWidth +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.io.ByteArrayOutputStream import java.lang.Exception import java.nio.ByteBuffer @@ -39,20 +42,22 @@ class OSBARCBarcodeAnalyzer( * @param image - ImageProxy object that represents the image to be analyzed. */ override fun analyze(image: ImageProxy) { - try { - scanLibrary.scanBarcode( - image, - cropBitmap(imageProxyToBitmap(image)), - { - onBarcodeScanned(it) - }, - { - onScanningError(it) - } - ) - } catch (e: Exception) { - e.message?.let { Log.e(LOG_TAG, it) } - onScanningError(OSBARCError.SCANNING_GENERAL_ERROR) + CoroutineScope(Dispatchers.Default).launch { + try { + scanLibrary.scanBarcode( + image, + cropBitmap(imageProxyToBitmap(image)), + { + onBarcodeScanned(it) + }, + { + onScanningError(it) + } + ) + } catch (e: Exception) { + e.message?.let { Log.e(LOG_TAG, it) } + onScanningError(OSBARCError.SCANNING_GENERAL_ERROR) + } } } @@ -67,7 +72,7 @@ class OSBARCBarcodeAnalyzer( * - https://developer.android.com/jetpack/androidx/releases/camera#1.3.0 * @param image - ImageProxy object that represents the image to be analyzed. */ - private fun imageProxyToBitmap(image: ImageProxy): Bitmap { + private suspend fun imageProxyToBitmap(image: ImageProxy): Bitmap { // get image data val planes = image.planes @@ -110,7 +115,7 @@ class OSBARCBarcodeAnalyzer( * It uses different ratios depending on the orientation of the device - portrait or landscape. * @param bitmap - Bitmap object to crop. */ - private fun cropBitmap(bitmap: Bitmap): Bitmap { + private suspend fun cropBitmap(bitmap: Bitmap): Bitmap { val rectWidth: Int val rectHeight: Int diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCMLKitWrapper.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCMLKitWrapper.kt index 6fa7a5e..8463b27 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCMLKitWrapper.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCMLKitWrapper.kt @@ -22,7 +22,7 @@ class OSBARCMLKitWrapper(private val helper: OSBARCMLKitHelperInterface): OSBARC * @param onSuccess - The code to be executed if the operation was successful. * @param onError - The code to be executed if the operation was not successful. */ - override fun scanBarcode( + override suspend fun scanBarcode( imageProxy: ImageProxy, imageBitmap: Bitmap, onSuccess: (String) -> Unit, diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCScanLibraryInterface.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCScanLibraryInterface.kt index 6b2bd15..9df6ee9 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCScanLibraryInterface.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCScanLibraryInterface.kt @@ -8,7 +8,7 @@ import com.outsystems.plugins.barcode.model.OSBARCError * Interface that provides the signature of the scanBarcode method */ fun interface OSBARCScanLibraryInterface { - fun scanBarcode( + suspend fun scanBarcode( imageProxy: ImageProxy, imageBitmap: Bitmap, onSuccess: (String) -> Unit, diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCZXingWrapper.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCZXingWrapper.kt index 8f20aa2..2fb85b1 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCZXingWrapper.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCZXingWrapper.kt @@ -22,7 +22,7 @@ class OSBARCZXingWrapper(private val helper: OSBARCZXingHelperInterface) : OSBAR * @param onSuccess - The code to be executed if the operation was successful. * @param onError - The code to be executed if the operation was not successful. */ - override fun scanBarcode( + override suspend fun scanBarcode( imageProxy: ImageProxy, imageBitmap: Bitmap, onSuccess: (String) -> Unit, diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelper.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelper.kt index 5cbab21..480c316 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelper.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelper.kt @@ -2,6 +2,8 @@ package com.outsystems.plugins.barcode.controller.helper import android.graphics.Bitmap import android.graphics.BitmapFactory +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext /** * Helper class that implements the OSBARCImageHelperInterface @@ -14,13 +16,14 @@ class OSBARCImageHelper: OSBARCImageHelperInterface { * @param imageBytes - ByteArray to convert * @return the resulting bitmap. */ - override fun bitmapFromImageBytes(imageBytes: ByteArray): Bitmap { - return BitmapFactory.decodeByteArray( - imageBytes, - 0, // use 0 in the offset to decode from the beginning of imageBytes - imageBytes.size // use byte array size as length because we want to decode the whole image - ) - } + override suspend fun bitmapFromImageBytes(imageBytes: ByteArray): Bitmap = + withContext(Dispatchers.Default) { + return@withContext BitmapFactory.decodeByteArray( + imageBytes, + 0, // use 0 in the offset to decode from the beginning of imageBytes + imageBytes.size // use byte array size as length because we want to decode the whole image + ) + } /** * Creates a bitmap that is a subset of the source bitmap, @@ -32,19 +35,21 @@ class OSBARCImageHelper: OSBARCImageHelperInterface { * @param rectHeight - Height of the bitmap. * @return the resulting bitmap. */ - override fun createSubsetBitmapFromSource( + override suspend fun createSubsetBitmapFromSource( source: Bitmap, rectLeft: Int, rectTop: Int, rectWidth: Int, rectHeight: Int - ): Bitmap { - return Bitmap.createBitmap( - source, - rectLeft, - rectTop, - rectWidth, - rectHeight - ) - } + ): Bitmap = + withContext(Dispatchers.Default) { + return@withContext Bitmap.createBitmap( + source, + rectLeft, + rectTop, + rectWidth, + rectHeight + ) + } + } \ No newline at end of file diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelperInterface.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelperInterface.kt index ea10b9a..446de5f 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelperInterface.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCImageHelperInterface.kt @@ -6,8 +6,8 @@ import android.graphics.Bitmap * Interface that provides the signature of the type's methods. */ interface OSBARCImageHelperInterface { - fun bitmapFromImageBytes(imageBytes: ByteArray): Bitmap - fun createSubsetBitmapFromSource( + suspend fun bitmapFromImageBytes(imageBytes: ByteArray): Bitmap + suspend fun createSubsetBitmapFromSource( source: Bitmap, rectLeft: Int, rectTop: Int, diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelper.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelper.kt index 0a095d3..d8c7f05 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelper.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelper.kt @@ -9,6 +9,8 @@ import com.google.zxing.MultiFormatReader import com.google.zxing.NotFoundException import com.google.zxing.RGBLuminanceSource import com.google.zxing.common.HybridBinarizer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext /** * Helper class that implements the OSBARCZXingHelperInterface @@ -27,22 +29,27 @@ class OSBARCZXingHelper: OSBARCZXingHelperInterface { * @param rotationDegrees - degrees to rotate the image. * @return the resulting bitmap. */ - override fun rotateBitmap(bitmap: Bitmap, rotationDegrees: Int): Bitmap { - // create a matrix for rotation - val matrix = Matrix() - matrix.postRotate(rotationDegrees.toFloat()) - - // actually rotate the image - return Bitmap.createBitmap( - bitmap, - 0, // 0 is the x coordinate of the first pixel in source bitmap - 0, // 0 is the y coordinate of the first pixel in source bitmap - bitmap.width, // number of pixels in each row - bitmap.height, // number of rows - matrix, // matrix to be used for rotation - true // true states that source bitmap should be filtered using matrix (rotation) - ) - } + override suspend fun rotateBitmap(bitmap: Bitmap, rotationDegrees: Int): Bitmap = + withContext(Dispatchers.Default) { + return@withContext try { + // create a matrix for rotation + val matrix = Matrix() + matrix.postRotate(rotationDegrees.toFloat()) + // actually rotate the image + Bitmap.createBitmap( + bitmap, + 0, // 0 is the x coordinate of the first pixel in source bitmap + 0, // 0 is the y coordinate of the first pixel in source bitmap + bitmap.width, // number of pixels in each row + bitmap.height, // number of rows + matrix, // matrix to be used for rotation + true // true states that source bitmap should be filtered using matrix (rotation) + ) + } finally { + // do nothing + // we need to use finally to avoid compilation error + } + } /** * Scans an image looking for barcodes, using the ZXing library. diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelperInterface.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelperInterface.kt index a482d79..293b0c3 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelperInterface.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/helper/OSBARCZXingHelperInterface.kt @@ -6,7 +6,7 @@ import android.graphics.Bitmap * Interface that provides the signature of the type's methods. */ interface OSBARCZXingHelperInterface { - fun rotateBitmap(bitmap: Bitmap, rotationDegrees: Int): Bitmap + suspend fun rotateBitmap(bitmap: Bitmap, rotationDegrees: Int): Bitmap fun decodeImage( pixels: IntArray, width: Int, diff --git a/src/test/kotlin/com/outsystems/plugins/barcode/ScanCodeTests.kt b/src/test/kotlin/com/outsystems/plugins/barcode/ScanCodeTests.kt index 5a98be8..2c852bd 100644 --- a/src/test/kotlin/com/outsystems/plugins/barcode/ScanCodeTests.kt +++ b/src/test/kotlin/com/outsystems/plugins/barcode/ScanCodeTests.kt @@ -17,6 +17,7 @@ import com.outsystems.plugins.barcode.mocks.OSBARCZXingHelperMock import com.outsystems.plugins.barcode.mocks.OSBARCScanLibraryMock import com.outsystems.plugins.barcode.model.OSBARCError import com.outsystems.plugins.barcode.model.OSBARCScanParameters +import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.fail import org.junit.Before @@ -429,16 +430,19 @@ class ScanCodeTests { Mockito.doReturn(90).`when`(mockImageInfo).rotationDegrees // do the same for 270 and 0 to cover all cases - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - assertEquals(SCAN_RESULT, it) - }, - { - fail() - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + assertEquals(SCAN_RESULT, it) + }, + { + fail() + } + ) + } + } @Test @@ -451,16 +455,19 @@ class ScanCodeTests { Mockito.doReturn(270).`when`(mockImageInfo).rotationDegrees // do the same for 270 and 0 to cover all cases - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - assertEquals(SCAN_RESULT, it) - }, - { - fail() - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + assertEquals(SCAN_RESULT, it) + }, + { + fail() + } + ) + } + } @Test @@ -473,16 +480,18 @@ class ScanCodeTests { Mockito.doReturn(0).`when`(mockImageInfo).rotationDegrees // do the same for 270 and 0 to cover all cases - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - assertEquals(SCAN_RESULT, it) - }, - { - fail() - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + assertEquals(SCAN_RESULT, it) + }, + { + fail() + } + ) + } } @Test @@ -498,17 +507,19 @@ class ScanCodeTests { Mockito.doReturn(0).`when`(mockImageInfo).rotationDegrees // do the same for 270 and 0 to cover all cases - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - fail() - }, - { - assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.code, it.code) - assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.description, it.description) - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + fail() + }, + { + assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.code, it.code) + assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.description, it.description) + } + ) + } } @Test @@ -524,17 +535,20 @@ class ScanCodeTests { Mockito.doReturn(0).`when`(mockImageInfo).rotationDegrees // do the same for 270 and 0 to cover all cases - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - fail() - }, - { - assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.code, it.code) - assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.description, it.description) - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + fail() + }, + { + assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.code, it.code) + assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.description, it.description) + } + ) + } + } @Test @@ -552,16 +566,19 @@ class ScanCodeTests { val mockMediaImage = Mockito.mock(Image::class.java) Mockito.doReturn(mockMediaImage).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - assertEquals(SCAN_RESULT, it) - }, - { - fail() - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + assertEquals(SCAN_RESULT, it) + }, + { + fail() + } + ) + } + } @Test @@ -579,16 +596,18 @@ class ScanCodeTests { val mockMediaImage = Mockito.mock(Image::class.java) Mockito.doReturn(mockMediaImage).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - // do nothing - }, - { - // do nothing - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + // do nothing + }, + { + // do nothing + } + ) + } } @Test @@ -606,16 +625,19 @@ class ScanCodeTests { val mockMediaImage = Mockito.mock(Image::class.java) Mockito.doReturn(mockMediaImage).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - // do nothing - }, - { - // do nothing - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + // do nothing + }, + { + // do nothing + } + ) + } + } @Test @@ -631,16 +653,18 @@ class ScanCodeTests { val mockMediaImage = Mockito.mock(Image::class.java) Mockito.doReturn(mockMediaImage).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - // do nothing - }, - { - // do nothing - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + // do nothing + }, + { + // do nothing + } + ) + } } @Test @@ -657,17 +681,19 @@ class ScanCodeTests { val mockMediaImage = Mockito.mock(Image::class.java) Mockito.doReturn(mockMediaImage).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - fail() - }, - { - assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.code, it.code) - assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.description, it.description) - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + fail() + }, + { + assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.code, it.code) + assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.description, it.description) + } + ) + } } @Test @@ -685,17 +711,19 @@ class ScanCodeTests { val mockMediaImage = Mockito.mock(Image::class.java) Mockito.doReturn(mockMediaImage).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - fail() - }, - { - assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.code, it.code) - assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.description, it.description) - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + fail() + }, + { + assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.code, it.code) + assertEquals(OSBARCError.MLKIT_LIBRARY_ERROR.description, it.description) + } + ) + } } @Test @@ -714,17 +742,19 @@ class ScanCodeTests { val mockMediaImage = Mockito.mock(Image::class.java) Mockito.doReturn(mockMediaImage).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - fail() - }, - { - assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.code, it.code) - assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.description, it.description) - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + fail() + }, + { + assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.code, it.code) + assertEquals(OSBARCError.ZXING_LIBRARY_ERROR.description, it.description) + } + ) + } } @Test @@ -739,16 +769,19 @@ class ScanCodeTests { Mockito.doReturn(null).`when`(mockImageProxy).image - wrapper.scanBarcode( - mockImageProxy, - mockBitmap, - { - // do nothing - }, - { - // do nothing - } - ) + runBlocking { + wrapper.scanBarcode( + mockImageProxy, + mockBitmap, + { + // do nothing + }, + { + // do nothing + } + ) + } + } } \ No newline at end of file diff --git a/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCImageHelperMock.kt b/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCImageHelperMock.kt index dd5b35f..161f242 100644 --- a/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCImageHelperMock.kt +++ b/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCImageHelperMock.kt @@ -5,11 +5,11 @@ import com.outsystems.plugins.barcode.controller.helper.OSBARCImageHelperInterfa import org.mockito.Mockito class OSBARCImageHelperMock: OSBARCImageHelperInterface { - override fun bitmapFromImageBytes(imageBytes: ByteArray): Bitmap { + override suspend fun bitmapFromImageBytes(imageBytes: ByteArray): Bitmap { return Mockito.mock(Bitmap::class.java) } - override fun createSubsetBitmapFromSource( + override suspend fun createSubsetBitmapFromSource( source: Bitmap, rectLeft: Int, rectTop: Int, diff --git a/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCScanLibraryMock.kt b/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCScanLibraryMock.kt index b1445a0..1e5e406 100644 --- a/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCScanLibraryMock.kt +++ b/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCScanLibraryMock.kt @@ -11,7 +11,7 @@ class OSBARCScanLibraryMock: OSBARCScanLibraryInterface { var success = true var exception = false var error: OSBARCError = OSBARCError.SCANNING_GENERAL_ERROR - override fun scanBarcode( + override suspend fun scanBarcode( imageProxy: ImageProxy, imageBitmap: Bitmap, onSuccess: (String) -> Unit, diff --git a/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCZXingHelperMock.kt b/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCZXingHelperMock.kt index 6b12777..be0ddec 100644 --- a/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCZXingHelperMock.kt +++ b/src/test/kotlin/com/outsystems/plugins/barcode/mocks/OSBARCZXingHelperMock.kt @@ -10,7 +10,7 @@ class OSBARCZXingHelperMock: OSBARCZXingHelperInterface { var success = true var exception = false - override fun rotateBitmap(bitmap: Bitmap, rotationDegrees: Int): Bitmap { + override suspend fun rotateBitmap(bitmap: Bitmap, rotationDegrees: Int): Bitmap { return Mockito.mock(Bitmap::class.java) }