Created
September 18, 2019 08:20
-
-
Save zamahaka/4f5326422e4edb4f1d2442837f876da4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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