Skip to content

Instantly share code, notes, and snippets.

@zamahaka
Created September 18, 2019 08:20
Show Gist options
  • Save zamahaka/4f5326422e4edb4f1d2442837f876da4 to your computer and use it in GitHub Desktop.
Save zamahaka/4f5326422e4edb4f1d2442837f876da4 to your computer and use it in GitHub Desktop.
package com.paragoncoin.paragon.core.ui.view
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.ViewGroup
import androidx.annotation.RequiresPermission
import com.google.android.gms.vision.CameraSource
import com.google.android.gms.vision.MultiProcessor
import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector
import com.paragoncoin.paragon.core.domain.barcode.BarcodeTrackerFactory
import java.io.IOException
@SuppressLint("MissingPermission")
class CameraBarcodePreviewView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {
private var cameraSource: CameraSource? = null
private var barcodeCallback: ((Barcode) -> Unit)? = null
private var surfaceAvailable = false
private val surfaceCallback = object : SurfaceHolder.Callback {
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
}
override fun surfaceCreated(holder: SurfaceHolder) {
refresh()
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
surfaceAvailable = false
}
}
private val surfaceView = SurfaceView(context)
private val holder get() = surfaceView.holder
private var startedByUser = false
init {
addView(surfaceView)
holder.addCallback(surfaceCallback)
holder.setSizeFromLayout()
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
val layoutWidth = right - left
val layoutHeight = bottom - top
// calculate surface view layout to avoid deformed picture
val (width, height) = cameraSource
?.takeUnless { it.previewSize == null }
?.let { source ->
val (previewWidth, previewHeight) = with(source.previewSize) { height to width }
return@let if (previewHeight * layoutWidth / previewWidth > layoutHeight) {
layoutWidth to (previewHeight * layoutWidth / previewWidth)
} else (previewWidth * layoutHeight / previewHeight) to layoutHeight
} ?: layoutWidth to layoutHeight
for (i in 0 until childCount) {
getChildAt(i).layout(0, 0, width, height)
}
startInternal()
}
@RequiresPermission(Manifest.permission.CAMERA)
fun start() {
startedByUser = true
startInternal()
}
fun stop() {
cameraSource?.stop()
}
fun release() = releaseSource()
fun onBarcodeDetected(handler: (Barcode) -> Unit) {
barcodeCallback = handler
}
fun startWithRefresh() {
refresh()
start()
}
private fun refresh() {
surfaceAvailable = true
refreshCamera()
requestLayout()
}
private fun startInternal() {
if (startedByUser) try {
if (surfaceAvailable) cameraSource?.start(holder)
} catch (e: IOException) {
releaseSource()
}
}
private fun releaseSource() {
try {
cameraSource?.release()
} catch (thr: Throwable) {
}
}
@RequiresPermission(Manifest.permission.CAMERA)
private fun refreshCamera() {
releaseSource()
cameraSource = createCameraSource()
startInternal()
}
private fun onBarcodeDetected(barcode: Barcode) {
(context as? Activity)?.runOnUiThread {
barcodeCallback?.invoke(barcode)
}
}
@RequiresPermission(Manifest.permission.CAMERA)
private fun createCameraSource() = CameraSource.Builder(context.applicationContext,
BarcodeDetector.Builder(context).build().apply {
setProcessor(MultiProcessor.Builder(
BarcodeTrackerFactory(::onBarcodeDetected)
).build())
})
.setFacing(CameraSource.CAMERA_FACING_BACK)
.setRequestedPreviewSize(1600, 1024)
.setRequestedFps(15.0f)
.setAutoFocusEnabled(true)
.build()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment