Reputation:
Hey there I want to flip my camera with an image button (e.g. btn_switch_camera) from back to front and vice-versa, help me!, this beautiful code has all basic things but no camera flip option so I want a flip camera imagebutton in this camera app , it has latest CameraX api it supports gradle 7.0.0 +. I just need some code to switch camera , when I did it on my own it gave me null error on image preview , so I deleted that mess and give you this original code (it must include front_camera_ icon switch to rear_camera_icon effect):
private val cameraExecutor = Executors.newSingleThreadExecutor()
private var imagePreview: Preview? = null
private var imageAnalysis: ImageAnalysis? = null
private var imageCapture: ImageCapture? = null
private var videoCapture: VideoCapture? = null
private lateinit var outputDirectory: File
private var cameraControl: CameraControl? = null
private var cameraInfo: CameraInfo? = null
private var linearZoom = 0f
private var recording = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = MainActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
if (allPermissionsGranted()) {
startCamera()
} else {
requestPermissions(
REQUIRED_PERMISSIONS,
REQUEST_CODE_PERMISSIONS
)
}
outputDirectory = getOutputDirectory()
binding.cameraCaptureButton.setOnClickListener {
takePicture()
}
initCameraModeSelector()
binding.cameraTorchButton.setOnClickListener {
toggleTorch()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
startCamera()
} else {
finish()
}
}
}
/**
* Check if all permission specified in the manifest have been granted
*/
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
cameraProviderFuture.addListener({
imagePreview = Preview.Builder().apply {
setTargetAspectRatio(AspectRatio.RATIO_16_9)
setTargetRotation(binding.previewView.display.rotation)
}.build()
imageAnalysis = ImageAnalysis.Builder().apply {
setImageQueueDepth(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
}.build()
imageAnalysis?.setAnalyzer(cameraExecutor, LuminosityAnalyzer())
imageCapture = ImageCapture.Builder().apply {
setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
setFlashMode(ImageCapture.FLASH_MODE_AUTO)
}.build()
videoCapture = VideoCapture.Builder().apply {
setTargetAspectRatio(AspectRatio.RATIO_16_9)
}.build()
val cameraProvider = cameraProviderFuture.get()
val camera = cameraProvider.bindToLifecycle(
this,
cameraSelector,
imagePreview,
// imageAnalysis,
imageCapture,
videoCapture
)
binding.previewView.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
imagePreview?.setSurfaceProvider(binding.previewView.surfaceProvider)
cameraControl = camera.cameraControl
cameraInfo = camera.cameraInfo
setTorchStateObserver()
setZoomStateObserver()
}, ContextCompat.getMainExecutor(this))
}
private fun setTorchStateObserver() {
cameraInfo?.torchState?.observe(this, { state ->
if (state == TorchState.ON) {
binding.cameraTorchButton.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.ic_flashlight_off_24dp
)
)
} else {
binding.cameraTorchButton.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.ic_flashlight_on_24dp
)
)
}
})
}
private fun setZoomStateObserver() {
cameraInfo?.zoomState?.observe(this, { state ->
// state.linearZoom
// state.zoomRatio
// state.maxZoomRatio
// state.minZoomRatio
Log.d(TAG, "${state.linearZoom}")
})
}
private fun initCameraModeSelector() {
binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {}
override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabSelected(tab: TabLayout.Tab?) {
when (tab?.position) {
PHOTO -> {
binding.cameraCaptureButton.setOnClickListener {
takePicture()
}
}
VIDEO -> {
binding.cameraCaptureButton.setOnClickListener {
if (recording) {
videoCapture?.stopRecording()
it.isSelected = false
recording = false
} else {
recordVideo()
it.isSelected = true
recording = true
}
}
}
}
}
})
}
private fun takePicture() {
val file = createFile(
outputDirectory,
FILENAME,
PHOTO_EXTENSION
)
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
imageCapture?.takePicture(outputFileOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val msg = "Photo capture succeeded: ${file.absolutePath}"
binding.previewView.post {
Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
}
}
override fun onError(exception: ImageCaptureException) {
val msg = "Photo capture failed: ${exception.message}"
binding.previewView.post {
Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
}
}
})
}
private fun recordVideo() {
val file = createFile(
outputDirectory,
FILENAME,
VIDEO_EXTENSION
)
val outputFileOptions = VideoCapture.OutputFileOptions.Builder(file).build()
videoCapture?.startRecording(outputFileOptions, cameraExecutor, object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
val msg = "Video capture succeeded: ${file.absolutePath}"
binding.previewView.post {
Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
}
}
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
val msg = "Video capture failed: $message"
binding.previewView.post {
Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
}
}
})
}
private fun toggleTorch() {
if (cameraInfo?.torchState?.value == TorchState.ON) {
cameraControl?.enableTorch(false)
} else {
cameraControl?.enableTorch(true)
}
}
// Manage camera Zoom
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
return when (keyCode) {
KeyEvent.KEYCODE_VOLUME_UP -> {
if (linearZoom <= 0.9) {
linearZoom += 0.1f
}
cameraControl?.setLinearZoom(linearZoom)
true
}
KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (linearZoom >= 0.1) {
linearZoom -= 0.1f
}
cameraControl?.setLinearZoom(linearZoom)
true
}
else -> super.onKeyDown(keyCode, event)
}
}
private class LuminosityAnalyzer : ImageAnalysis.Analyzer {
private var lastAnalyzedTimestamp = 0L
/**
* Helper extension function used to extract a byte array from an
* image plane buffer
*/
private fun ByteBuffer.toByteArray(): ByteArray {
rewind() // Rewind the buffer to zero
val data = ByteArray(remaining())
get(data) // Copy the buffer into a byte array
return data // Return the byte array
}
override fun analyze(image: ImageProxy) {
image.imageInfo.rotationDegrees
val currentTimestamp = System.currentTimeMillis()
// Calculate the average luma no more often than every second
if (currentTimestamp - lastAnalyzedTimestamp >=
TimeUnit.SECONDS.toMillis(1)
) {
// Since format in ImageAnalysis is YUV, image.planes[0]
// contains the Y (luminance) plane
val buffer = image.planes[0].buffer
// Extract image data from callback object
val data = buffer.toByteArray()
// Convert the data into an array of pixel values
val pixels = data.map { it.toInt() and 0xFF }
// Compute average luminance for the image
val luma = pixels.average()
// Log the new luma value
Log.d("CameraXApp", "Average luminosity: $luma")
// Update timestamp of last analyzed frame
lastAnalyzedTimestamp = currentTimestamp
}
image.close()
}
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
private fun getOutputDirectory(): File {
// TODO: 29/01/2021 Remove externalMediaDirs (deprecated)
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir != null && mediaDir.exists())
mediaDir else filesDir
}
companion object {
private const val TAG = "MainActivity"
private const val FILENAME = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val PHOTO_EXTENSION = ".jpg"
private const val VIDEO_EXTENSION = ".mp4"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
private const val PHOTO = 0
private const val VIDEO = 1
fun createFile(baseFolder: File, format: String, extension: String) =
File(
baseFolder, SimpleDateFormat(format, Locale.US)
.format(System.currentTimeMillis()) + extension
)
}
} Footer © 2022 GitHub, Inc. Footer navigation Terms Privacy Security Status Docs Contact GitHub Pricing API Training Blog About
Upvotes: 1
Views: 684
Reputation: 203
You can use cameraX Library.
In CameraSelector you can use requireLensFacing(int lensFacing)
to flip the camera.
Here is the code that copied from here:
private var lensFacing = CameraX.LensFacing.BACK
private var imageCapture: ImageCapture? = null
@SuppressLint("RestrictedApi")
private fun startCamera() {
bindCameraUseCases()
// Listener for button used to switch cameras
switchButton = view.findViewById(R.id.switch_button)
switchButton.setOnClickListener {
lensFacing = if (CameraX.LensFacing.FRONT == lensFacing) {
CameraX.LensFacing.BACK
} else {
CameraX.LensFacing.FRONT
}
try {
// Only bind use cases if we can query a camera with this orientation
CameraX.getCameraWithLensFacing(lensFacing)
bindCameraUseCases()
} catch (exc: Exception) {
// Do nothing
}
}
}
private fun bindCameraUseCases() {
// Make sure that there are no other use cases bound to CameraX
CameraX.unbindAll()
val previewConfig = PreviewConfig.Builder().apply {
setLensFacing(lensFacing)
}.build()
val preview = Preview(previewConfig)
val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
setLensFacing(lensFacing)
}.build()
imageCapture = ImageCapture(imageCaptureConfig)
// Apply declared configs to CameraX using the same lifecycle owner
CameraX.bindToLifecycle(this, preview, imageCapture)
}
Upvotes: 1