Skip to content

Commit

Permalink
[Android] Replaced play-services-vision with mlkit:barcode-scanning (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
panliming-tuya authored and pull[bot] committed Nov 22, 2022
1 parent d662734 commit 2542401
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 282 deletions.
5 changes: 5 additions & 0 deletions examples/android/CHIPTool/.idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions examples/android/CHIPTool/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 30
compileSdkVersion 31

defaultConfig {
applicationId "com.google.chip.chiptool"
minSdkVersion 24
targetSdkVersion 30
targetSdkVersion 31
versionCode 1
versionName "1.0"

Expand Down Expand Up @@ -82,7 +82,6 @@ dependencies {

implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.1'
implementation "com.google.android.gms:play-services-vision:20.1.0"
implementation 'androidx.fragment:fragment:1.3.0-beta01'
implementation "androidx.annotation:annotation:1.1.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
Expand All @@ -96,6 +95,13 @@ dependencies {
implementation "androidx.work:work-runtime:2.3.3"
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.jjoe64:graphview:4.2.2'

implementation 'com.google.mlkit:barcode-scanning:17.0.2'
def camerax_version = "1.1.0"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
}
repositories {
mavenCentral()
Expand Down
1 change: 1 addition & 0 deletions examples/android/CHIPTool/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<activity
android:name=".CHIPToolActivity"
android:label="@string/app_name"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,52 @@ import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.annotation.RequiresPermission
import androidx.appcompat.app.AlertDialog
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.fragment.app.Fragment
import chip.setuppayload.SetupPayload
import chip.setuppayload.SetupPayloadParser
import chip.setuppayload.SetupPayloadParser.SetupPayloadException
import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException
import com.google.android.gms.vision.CameraSource
import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector
import com.google.chip.chiptool.R
import com.google.chip.chiptool.SelectActionFragment
import com.google.chip.chiptool.util.FragmentUtil
import java.io.IOException
import com.google.mlkit.vision.barcode.BarcodeScanner
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import kotlinx.android.synthetic.main.barcode_fragment.view.inputAddressBtn
import java.util.concurrent.Executors
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/** Launches the camera to scan for QR code. */
class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListener {

private var cameraSource: CameraSource? = null
private var cameraSourceView: CameraSourceView? = null
private var barcodeDetector: BarcodeDetector? = null
private var cameraStarted = false
class BarcodeFragment : Fragment() {

private lateinit var previewView: PreviewView
private var manualCodeEditText: EditText? = null
private var manualCodeBtn: Button? = null

private fun aspectRatio(width: Int, height: Int): Int {
val previewRatio = max(width, height).toDouble() / min(width, height)
if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
return AspectRatio.RATIO_4_3
}
return AspectRatio.RATIO_16_9
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!hasCameraPermission()) {
Expand All @@ -72,11 +83,10 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.barcode_fragment, container, false).apply {
cameraSourceView = findViewById(R.id.camera_view)

previewView = findViewById(R.id.camera_view)
manualCodeEditText = findViewById(R.id.manualCodeEditText)
manualCodeBtn = findViewById(R.id.manualCodeBtn)

startCamera()
inputAddressBtn.setOnClickListener {
FragmentUtil.getHost(
this@BarcodeFragment,
Expand All @@ -86,46 +96,76 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initializeBarcodeDetectorAndCamera()
}
@SuppressLint("UnsafeOptInUsageError")
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireActivity())
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
val metrics = DisplayMetrics().also { previewView.display?.getRealMetrics(it) }
val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
// Preview
val preview: Preview = Preview.Builder()
.setTargetAspectRatio(screenAspectRatio)
.setTargetRotation(previewView.display.rotation)
.build()
preview.setSurfaceProvider(previewView.surfaceProvider)

@SuppressLint("MissingPermission")
override fun onResume() {
super.onResume()
// Setup barcode scanner
val imageAnalysis = ImageAnalysis.Builder()
.setTargetAspectRatio(screenAspectRatio)
.setTargetRotation(previewView.display.rotation)
.build()
val cameraExecutor = Executors.newSingleThreadExecutor()
val barcodeScanner: BarcodeScanner = BarcodeScanning.getClient()
imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
processImageProxy(barcodeScanner, imageProxy)
}
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()

if (hasCameraPermission() && !cameraStarted) {
startCamera()
}
}
// Bind use cases to camera
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)

private fun initializeBarcodeDetectorAndCamera() {
barcodeDetector?.let { detector ->
if (!detector.isOperational) {
showCameraUnavailableAlert()
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
return
}

val context = requireContext()
barcodeDetector = BarcodeDetector.Builder(context).build().apply {
setProcessor(CHIPBarcodeProcessor(this@BarcodeFragment))
}
cameraSource = CameraSource.Builder(context, barcodeDetector)
.setFacing(CameraSource.CAMERA_FACING_BACK)
.setAutoFocusEnabled(true)
.setRequestedFps(30.0f)
.build()
}, ContextCompat.getMainExecutor(requireActivity()))

//workaround: can not use gms to scan the code in China, added a EditText to debug
manualCodeBtn?.setOnClickListener {
var qrCode = manualCodeEditText?.text.toString()
val qrCode = manualCodeEditText?.text.toString()
Log.d(TAG, "Submit Code:$qrCode")
handleInputQrCode(qrCode)
}
}

@ExperimentalGetImage
private fun processImageProxy(
barcodeScanner: BarcodeScanner,
imageProxy: ImageProxy
) {
val inputImage =
InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees)

barcodeScanner.process(inputImage)
.addOnSuccessListener { barcodes ->
barcodes.forEach {
handleScannedQrCode(it)
}
}
.addOnFailureListener {
Log.e(TAG, it.message ?: it.toString())
}.addOnCompleteListener {
// When the image is from CameraX analysis use case, must call image.close() on received
// images when finished using them. Otherwise, new images may not be received or the camera
// may stall.
imageProxy.close()
}
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
Expand All @@ -146,63 +186,27 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
payload = SetupPayloadParser().parseQrCode(qrCode)
} catch (ex: UnrecognizedQrCodeException) {
Log.e(TAG, "Unrecognized QR Code", ex)
Toast.makeText(requireContext(), "Unrecognized QR Code : ${ex.message}", Toast.LENGTH_SHORT).show()
payload = SetupPayload()
return
} catch (ex: SetupPayloadException) {
Log.e(TAG, "Exception ", ex)
Toast.makeText(requireContext(), "Exception : ${ex.message}", Toast.LENGTH_SHORT).show()
payload = SetupPayload()
return
Toast.makeText(requireContext(), "Unrecognized QR Code", Toast.LENGTH_SHORT).show()
}
FragmentUtil.getHost(this, Callback::class.java)
?.onCHIPDeviceInfoReceived(CHIPDeviceInfo.fromSetupPayload(payload))
}

@SuppressLint("MissingPermission")
override fun handleScannedQrCode(barcode: Barcode) {
private fun handleScannedQrCode(barcode: Barcode) {
Handler(Looper.getMainLooper()).post {
stopCamera()

lateinit var payload: SetupPayload
try {
payload = SetupPayloadParser().parseQrCode(barcode.displayValue)
} catch (ex: UnrecognizedQrCodeException) {
Log.e(TAG, "Unrecognized QR Code", ex)
Toast.makeText(requireContext(), "Unrecognized QR Code", Toast.LENGTH_SHORT).show()

// Restart camera view.
if (hasCameraPermission() && !cameraStarted) {
startCamera()
}
payload = SetupPayload()
return@post
} catch (ex: SetupPayloadException) {
Log.e(TAG, "Exception ", ex)
Toast.makeText(requireContext(), "Exception : ${ex.message}", Toast.LENGTH_SHORT).show()

// Restart camera view.
if (hasCameraPermission() && !cameraStarted) {
startCamera()
}
payload = SetupPayload()
return@post
}
FragmentUtil.getHost(this, Callback::class.java)
?.onCHIPDeviceInfoReceived(CHIPDeviceInfo.fromSetupPayload(payload))
}
}

override fun onPause() {
super.onPause()
stopCamera()
}

override fun onDestroy() {
super.onDestroy()
cameraSourceView?.release()
}

private fun showCameraPermissionAlert() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.camera_permission_missing_alert_title)
Expand All @@ -227,24 +231,9 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
.show()
}

@RequiresPermission(Manifest.permission.CAMERA)
private fun startCamera() {
try {
cameraSourceView?.start(cameraSource)
cameraStarted = true
} catch (e: IOException) {
Log.e(TAG, "Unable to start camera source.", e)
}
}

private fun stopCamera() {
cameraSourceView?.stop()
cameraStarted = false
}

private fun hasCameraPermission(): Boolean {
return (PackageManager.PERMISSION_GRANTED
== checkSelfPermission(requireContext(), Manifest.permission.CAMERA))
== checkSelfPermission(requireContext(), Manifest.permission.CAMERA))
}

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

@JvmStatic fun newInstance() = BarcodeFragment()
@JvmStatic
fun newInstance() = BarcodeFragment()

private const val RATIO_4_3_VALUE = 4.0 / 3.0
private const val RATIO_16_9_VALUE = 16.0 / 9.0
}
}
}

This file was deleted.

Loading

0 comments on commit 2542401

Please sign in to comment.