Skip to content

Commit 1554133

Browse files
panliming-tuyapull[bot]
authored andcommitted
[Android] Replaced play-services-vision with mlkit:barcode-scanning (#23090)
1 parent 4c204f5 commit 1554133

File tree

7 files changed

+108
-282
lines changed

7 files changed

+108
-282
lines changed

examples/android/CHIPTool/.idea/jarRepositories.xml

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/android/CHIPTool/app/build.gradle

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ apply plugin: 'kotlin-android'
33
apply plugin: 'kotlin-android-extensions'
44

55
android {
6-
compileSdkVersion 30
6+
compileSdkVersion 31
77

88
defaultConfig {
99
applicationId "com.google.chip.chiptool"
1010
minSdkVersion 24
11-
targetSdkVersion 30
11+
targetSdkVersion 31
1212
versionCode 1
1313
versionName "1.0"
1414

@@ -82,7 +82,6 @@ dependencies {
8282

8383
implementation 'androidx.appcompat:appcompat:1.1.0'
8484
implementation 'androidx.preference:preference:1.1.1'
85-
implementation "com.google.android.gms:play-services-vision:20.1.0"
8685
implementation 'androidx.fragment:fragment:1.3.0-beta01'
8786
implementation "androidx.annotation:annotation:1.1.0"
8887
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
@@ -96,6 +95,13 @@ dependencies {
9695
implementation "androidx.work:work-runtime:2.3.3"
9796
implementation 'com.google.code.gson:gson:2.8.5'
9897
implementation 'com.jjoe64:graphview:4.2.2'
98+
99+
implementation 'com.google.mlkit:barcode-scanning:17.0.2'
100+
def camerax_version = "1.1.0"
101+
implementation "androidx.camera:camera-core:${camerax_version}"
102+
implementation "androidx.camera:camera-camera2:${camerax_version}"
103+
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
104+
implementation "androidx.camera:camera-view:${camerax_version}"
99105
}
100106
repositories {
101107
mavenCentral()

examples/android/CHIPTool/app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<activity
2424
android:name=".CHIPToolActivity"
2525
android:label="@string/app_name"
26+
android:exported="true"
2627
android:windowSoftInputMode="adjustResize">
2728
<intent-filter>
2829
<action android:name="android.intent.action.MAIN" />

examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/BarcodeFragment.kt

+92-99
Original file line numberDiff line numberDiff line change
@@ -24,41 +24,52 @@ import android.content.pm.PackageManager
2424
import android.os.Bundle
2525
import android.os.Handler
2626
import android.os.Looper
27+
import android.util.DisplayMetrics
2728
import android.util.Log
2829
import android.view.LayoutInflater
2930
import android.view.View
3031
import android.view.ViewGroup
3132
import android.widget.Button
3233
import android.widget.EditText
3334
import android.widget.Toast
34-
import androidx.annotation.RequiresPermission
3535
import androidx.appcompat.app.AlertDialog
36+
import androidx.camera.core.*
37+
import androidx.camera.lifecycle.ProcessCameraProvider
38+
import androidx.camera.view.PreviewView
39+
import androidx.core.content.ContextCompat
3640
import androidx.core.content.ContextCompat.checkSelfPermission
3741
import androidx.fragment.app.Fragment
3842
import chip.setuppayload.SetupPayload
3943
import chip.setuppayload.SetupPayloadParser
40-
import chip.setuppayload.SetupPayloadParser.SetupPayloadException
4144
import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException
42-
import com.google.android.gms.vision.CameraSource
43-
import com.google.android.gms.vision.barcode.Barcode
44-
import com.google.android.gms.vision.barcode.BarcodeDetector
4545
import com.google.chip.chiptool.R
4646
import com.google.chip.chiptool.SelectActionFragment
4747
import com.google.chip.chiptool.util.FragmentUtil
48-
import java.io.IOException
48+
import com.google.mlkit.vision.barcode.BarcodeScanner
49+
import com.google.mlkit.vision.barcode.BarcodeScanning
50+
import com.google.mlkit.vision.barcode.common.Barcode
51+
import com.google.mlkit.vision.common.InputImage
4952
import kotlinx.android.synthetic.main.barcode_fragment.view.inputAddressBtn
53+
import java.util.concurrent.Executors
54+
import kotlin.math.abs
55+
import kotlin.math.max
56+
import kotlin.math.min
5057

5158
/** Launches the camera to scan for QR code. */
52-
class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListener {
53-
54-
private var cameraSource: CameraSource? = null
55-
private var cameraSourceView: CameraSourceView? = null
56-
private var barcodeDetector: BarcodeDetector? = null
57-
private var cameraStarted = false
59+
class BarcodeFragment : Fragment() {
5860

61+
private lateinit var previewView: PreviewView
5962
private var manualCodeEditText: EditText? = null
6063
private var manualCodeBtn: Button? = null
6164

65+
private fun aspectRatio(width: Int, height: Int): Int {
66+
val previewRatio = max(width, height).toDouble() / min(width, height)
67+
if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
68+
return AspectRatio.RATIO_4_3
69+
}
70+
return AspectRatio.RATIO_16_9
71+
}
72+
6273
override fun onCreate(savedInstanceState: Bundle?) {
6374
super.onCreate(savedInstanceState)
6475
if (!hasCameraPermission()) {
@@ -72,11 +83,10 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
7283
savedInstanceState: Bundle?
7384
): View {
7485
return inflater.inflate(R.layout.barcode_fragment, container, false).apply {
75-
cameraSourceView = findViewById(R.id.camera_view)
76-
86+
previewView = findViewById(R.id.camera_view)
7787
manualCodeEditText = findViewById(R.id.manualCodeEditText)
7888
manualCodeBtn = findViewById(R.id.manualCodeBtn)
79-
89+
startCamera()
8090
inputAddressBtn.setOnClickListener {
8191
FragmentUtil.getHost(
8292
this@BarcodeFragment,
@@ -86,46 +96,76 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
8696
}
8797
}
8898

89-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
90-
super.onViewCreated(view, savedInstanceState)
91-
initializeBarcodeDetectorAndCamera()
92-
}
99+
@SuppressLint("UnsafeOptInUsageError")
100+
private fun startCamera() {
101+
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireActivity())
102+
cameraProviderFuture.addListener({
103+
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
104+
val metrics = DisplayMetrics().also { previewView.display?.getRealMetrics(it) }
105+
val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
106+
// Preview
107+
val preview: Preview = Preview.Builder()
108+
.setTargetAspectRatio(screenAspectRatio)
109+
.setTargetRotation(previewView.display.rotation)
110+
.build()
111+
preview.setSurfaceProvider(previewView.surfaceProvider)
93112

94-
@SuppressLint("MissingPermission")
95-
override fun onResume() {
96-
super.onResume()
113+
// Setup barcode scanner
114+
val imageAnalysis = ImageAnalysis.Builder()
115+
.setTargetAspectRatio(screenAspectRatio)
116+
.setTargetRotation(previewView.display.rotation)
117+
.build()
118+
val cameraExecutor = Executors.newSingleThreadExecutor()
119+
val barcodeScanner: BarcodeScanner = BarcodeScanning.getClient()
120+
imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
121+
processImageProxy(barcodeScanner, imageProxy)
122+
}
123+
// Select back camera as a default
124+
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
125+
try {
126+
// Unbind use cases before rebinding
127+
cameraProvider.unbindAll()
97128

98-
if (hasCameraPermission() && !cameraStarted) {
99-
startCamera()
100-
}
101-
}
129+
// Bind use cases to camera
130+
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)
102131

103-
private fun initializeBarcodeDetectorAndCamera() {
104-
barcodeDetector?.let { detector ->
105-
if (!detector.isOperational) {
106-
showCameraUnavailableAlert()
132+
} catch (exc: Exception) {
133+
Log.e(TAG, "Use case binding failed", exc)
107134
}
108-
return
109-
}
110-
111-
val context = requireContext()
112-
barcodeDetector = BarcodeDetector.Builder(context).build().apply {
113-
setProcessor(CHIPBarcodeProcessor(this@BarcodeFragment))
114-
}
115-
cameraSource = CameraSource.Builder(context, barcodeDetector)
116-
.setFacing(CameraSource.CAMERA_FACING_BACK)
117-
.setAutoFocusEnabled(true)
118-
.setRequestedFps(30.0f)
119-
.build()
135+
}, ContextCompat.getMainExecutor(requireActivity()))
120136

121137
//workaround: can not use gms to scan the code in China, added a EditText to debug
122138
manualCodeBtn?.setOnClickListener {
123-
var qrCode = manualCodeEditText?.text.toString()
139+
val qrCode = manualCodeEditText?.text.toString()
124140
Log.d(TAG, "Submit Code:$qrCode")
125141
handleInputQrCode(qrCode)
126142
}
127143
}
128144

145+
@ExperimentalGetImage
146+
private fun processImageProxy(
147+
barcodeScanner: BarcodeScanner,
148+
imageProxy: ImageProxy
149+
) {
150+
val inputImage =
151+
InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees)
152+
153+
barcodeScanner.process(inputImage)
154+
.addOnSuccessListener { barcodes ->
155+
barcodes.forEach {
156+
handleScannedQrCode(it)
157+
}
158+
}
159+
.addOnFailureListener {
160+
Log.e(TAG, it.message ?: it.toString())
161+
}.addOnCompleteListener {
162+
// When the image is from CameraX analysis use case, must call image.close() on received
163+
// images when finished using them. Otherwise, new images may not be received or the camera
164+
// may stall.
165+
imageProxy.close()
166+
}
167+
}
168+
129169
override fun onRequestPermissionsResult(
130170
requestCode: Int,
131171
permissions: Array<String>,
@@ -146,63 +186,27 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
146186
payload = SetupPayloadParser().parseQrCode(qrCode)
147187
} catch (ex: UnrecognizedQrCodeException) {
148188
Log.e(TAG, "Unrecognized QR Code", ex)
149-
Toast.makeText(requireContext(), "Unrecognized QR Code : ${ex.message}", Toast.LENGTH_SHORT).show()
150-
payload = SetupPayload()
151-
return
152-
} catch (ex: SetupPayloadException) {
153-
Log.e(TAG, "Exception ", ex)
154-
Toast.makeText(requireContext(), "Exception : ${ex.message}", Toast.LENGTH_SHORT).show()
155-
payload = SetupPayload()
156-
return
189+
Toast.makeText(requireContext(), "Unrecognized QR Code", Toast.LENGTH_SHORT).show()
157190
}
158191
FragmentUtil.getHost(this, Callback::class.java)
159192
?.onCHIPDeviceInfoReceived(CHIPDeviceInfo.fromSetupPayload(payload))
160193
}
161194

162-
@SuppressLint("MissingPermission")
163-
override fun handleScannedQrCode(barcode: Barcode) {
195+
private fun handleScannedQrCode(barcode: Barcode) {
164196
Handler(Looper.getMainLooper()).post {
165-
stopCamera()
166-
167197
lateinit var payload: SetupPayload
168198
try {
169199
payload = SetupPayloadParser().parseQrCode(barcode.displayValue)
170200
} catch (ex: UnrecognizedQrCodeException) {
171201
Log.e(TAG, "Unrecognized QR Code", ex)
172202
Toast.makeText(requireContext(), "Unrecognized QR Code", Toast.LENGTH_SHORT).show()
173-
174-
// Restart camera view.
175-
if (hasCameraPermission() && !cameraStarted) {
176-
startCamera()
177-
}
178-
payload = SetupPayload()
179-
return@post
180-
} catch (ex: SetupPayloadException) {
181-
Log.e(TAG, "Exception ", ex)
182-
Toast.makeText(requireContext(), "Exception : ${ex.message}", Toast.LENGTH_SHORT).show()
183-
184-
// Restart camera view.
185-
if (hasCameraPermission() && !cameraStarted) {
186-
startCamera()
187-
}
188-
payload = SetupPayload()
189203
return@post
190204
}
191205
FragmentUtil.getHost(this, Callback::class.java)
192206
?.onCHIPDeviceInfoReceived(CHIPDeviceInfo.fromSetupPayload(payload))
193207
}
194208
}
195209

196-
override fun onPause() {
197-
super.onPause()
198-
stopCamera()
199-
}
200-
201-
override fun onDestroy() {
202-
super.onDestroy()
203-
cameraSourceView?.release()
204-
}
205-
206210
private fun showCameraPermissionAlert() {
207211
AlertDialog.Builder(requireContext())
208212
.setTitle(R.string.camera_permission_missing_alert_title)
@@ -227,24 +231,9 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
227231
.show()
228232
}
229233

230-
@RequiresPermission(Manifest.permission.CAMERA)
231-
private fun startCamera() {
232-
try {
233-
cameraSourceView?.start(cameraSource)
234-
cameraStarted = true
235-
} catch (e: IOException) {
236-
Log.e(TAG, "Unable to start camera source.", e)
237-
}
238-
}
239-
240-
private fun stopCamera() {
241-
cameraSourceView?.stop()
242-
cameraStarted = false
243-
}
244-
245234
private fun hasCameraPermission(): Boolean {
246235
return (PackageManager.PERMISSION_GRANTED
247-
== checkSelfPermission(requireContext(), Manifest.permission.CAMERA))
236+
== checkSelfPermission(requireContext(), Manifest.permission.CAMERA))
248237
}
249238

250239
private fun requestCameraPermission() {
@@ -262,6 +251,10 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
262251
private const val TAG = "BarcodeFragment"
263252
private const val REQUEST_CODE_CAMERA_PERMISSION = 100;
264253

265-
@JvmStatic fun newInstance() = BarcodeFragment()
254+
@JvmStatic
255+
fun newInstance() = BarcodeFragment()
256+
257+
private const val RATIO_4_3_VALUE = 4.0 / 3.0
258+
private const val RATIO_16_9_VALUE = 16.0 / 9.0
266259
}
267-
}
260+
}

examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPBarcodeProcessor.kt

-48
This file was deleted.

0 commit comments

Comments
 (0)