Julian Steenbakker
Committed by GitHub

Merge pull request #1299 from yujune/fix-android-jank-ui

fix: fix android jank ui when returnImage is true
  1 +## 6.0.4
  2 +Bugs fixed:
  3 +* [Android] Fixed UI jank when `returnImage` is true.
  4 +
1 ## 6.0.3 5 ## 6.0.3
2 New features: 6 New features:
3 * Adds pause function to pause the camera but keep textures in place. 7 * Adds pause function to pause the camera but keep textures in place.
@@ -79,7 +79,8 @@ dependencies { @@ -79,7 +79,8 @@ dependencies {
79 79
80 implementation 'androidx.camera:camera-lifecycle:1.3.4' 80 implementation 'androidx.camera:camera-lifecycle:1.3.4'
81 implementation 'androidx.camera:camera-camera2:1.3.4' 81 implementation 'androidx.camera:camera-camera2:1.3.4'
  82 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
82 83
83 testImplementation 'org.jetbrains.kotlin:kotlin-test' 84 testImplementation 'org.jetbrains.kotlin:kotlin-test'
84 - testImplementation 'org.mockito:mockito-core:5.12.0' 85 + testImplementation 'org.mockito:mockito-core:5.12.0'
85 } 86 }
@@ -35,6 +35,10 @@ import dev.steenbakker.mobile_scanner.objects.DetectionSpeed @@ -35,6 +35,10 @@ import dev.steenbakker.mobile_scanner.objects.DetectionSpeed
35 import dev.steenbakker.mobile_scanner.objects.MobileScannerStartParameters 35 import dev.steenbakker.mobile_scanner.objects.MobileScannerStartParameters
36 import dev.steenbakker.mobile_scanner.utils.YuvToRgbConverter 36 import dev.steenbakker.mobile_scanner.utils.YuvToRgbConverter
37 import io.flutter.view.TextureRegistry 37 import io.flutter.view.TextureRegistry
  38 +import kotlinx.coroutines.CoroutineScope
  39 +import kotlinx.coroutines.Dispatchers
  40 +import kotlinx.coroutines.coroutineScope
  41 +import kotlinx.coroutines.launch
38 import java.io.ByteArrayOutputStream 42 import java.io.ByteArrayOutputStream
39 import kotlin.math.roundToInt 43 import kotlin.math.roundToInt
40 44
@@ -97,6 +101,7 @@ class MobileScanner( @@ -97,6 +101,7 @@ class MobileScanner(
97 101
98 if (newScannedBarcodes == lastScanned) { 102 if (newScannedBarcodes == lastScanned) {
99 // New scanned is duplicate, returning 103 // New scanned is duplicate, returning
  104 + imageProxy.close()
100 return@addOnSuccessListener 105 return@addOnSuccessListener
101 } 106 }
102 if (newScannedBarcodes.isNotEmpty()) { 107 if (newScannedBarcodes.isNotEmpty()) {
@@ -118,10 +123,12 @@ class MobileScanner( @@ -118,10 +123,12 @@ class MobileScanner(
118 } 123 }
119 124
120 if (barcodeMap.isEmpty()) { 125 if (barcodeMap.isEmpty()) {
  126 + imageProxy.close()
121 return@addOnSuccessListener 127 return@addOnSuccessListener
122 } 128 }
123 129
124 if (!returnImage) { 130 if (!returnImage) {
  131 + imageProxy.close()
125 mobileScannerCallback( 132 mobileScannerCallback(
126 barcodeMap, 133 barcodeMap,
127 null, 134 null,
@@ -130,31 +137,36 @@ class MobileScanner( @@ -130,31 +137,36 @@ class MobileScanner(
130 return@addOnSuccessListener 137 return@addOnSuccessListener
131 } 138 }
132 139
133 - val bitmap = Bitmap.createBitmap(mediaImage.width, mediaImage.height, Bitmap.Config.ARGB_8888)  
134 - val imageFormat = YuvToRgbConverter(activity.applicationContext) 140 + CoroutineScope(Dispatchers.IO).launch {
  141 + val bitmap = Bitmap.createBitmap(mediaImage.width, mediaImage.height, Bitmap.Config.ARGB_8888)
  142 + val imageFormat = YuvToRgbConverter(activity.applicationContext)
135 143
136 - imageFormat.yuvToRgb(mediaImage, bitmap) 144 + imageFormat.yuvToRgb(mediaImage, bitmap)
137 145
138 - val bmResult = rotateBitmap(bitmap, camera?.cameraInfo?.sensorRotationDegrees?.toFloat() ?: 90f) 146 + val bmResult = rotateBitmap(bitmap, camera?.cameraInfo?.sensorRotationDegrees?.toFloat() ?: 90f)
139 147
140 - val stream = ByteArrayOutputStream()  
141 - bmResult.compress(Bitmap.CompressFormat.PNG, 100, stream)  
142 - val byteArray = stream.toByteArray()  
143 - val bmWidth = bmResult.width  
144 - val bmHeight = bmResult.height  
145 - bmResult.recycle() 148 + val stream = ByteArrayOutputStream()
  149 + bmResult.compress(Bitmap.CompressFormat.PNG, 100, stream)
  150 + val byteArray = stream.toByteArray()
  151 + val bmWidth = bmResult.width
  152 + val bmHeight = bmResult.height
  153 +
  154 + bmResult.recycle()
  155 + imageProxy.close()
  156 +
  157 + mobileScannerCallback(
  158 + barcodeMap,
  159 + byteArray,
  160 + bmWidth,
  161 + bmHeight
  162 + )
  163 + }
146 164
147 - mobileScannerCallback(  
148 - barcodeMap,  
149 - byteArray,  
150 - bmWidth,  
151 - bmHeight  
152 - )  
153 }.addOnFailureListener { e -> 165 }.addOnFailureListener { e ->
154 mobileScannerErrorCallback( 166 mobileScannerErrorCallback(
155 e.localizedMessage ?: e.toString() 167 e.localizedMessage ?: e.toString()
156 ) 168 )
157 - }.addOnCompleteListener { imageProxy.close() } 169 + }
158 } 170 }
159 171
160 if (detectionSpeed == DetectionSpeed.NORMAL) { 172 if (detectionSpeed == DetectionSpeed.NORMAL) {