Android CameraX takePicture does not work from android codelab - android

I was running this code taken from "Getting Started with CameraX" codelab. When I press button I cant see anything from screen or logcat. After that I tried to add external storage write and read permission but it did not change anything. Source codes:
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.camerax_codelab_sample">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.Camerax_codelab_sample">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity
class MainActivity : AppCompatActivity() {
private var imageCapture: ImageCapture? = null
private lateinit var outputDirectory: File
private lateinit var cameraExecutor: ExecutorService
private lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(LayoutInflater.from(this))
setContentView(activityMainBinding.root)
// Request camera permissions
if (allPermissionsGranted()) {
startCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
// Set up the listener for take photo button
activityMainBinding.cameraCaptureButton.setOnClickListener { takePhoto() }
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun takePhoto() {
Log.d(TAG, "takePhoto inside")
val imageCapture = imageCapture ?: return
// Create time-stamped output file to hold the image
val photoFile = File(outputDirectory, SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(System.currentTimeMillis()) + ".jpg")
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Set up image capture listener, which is triggered after photo has
// been taken
imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
})
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable { // Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(activityMainBinding.viewFinder.surfaceProvider)
}
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try { // Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(this, cameraSelector, preview)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir != null && mediaDir.exists()) mediaDir else filesDir
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
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 {
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
finish()
}
}
}
companion object {
private const val TAG = "CameraXBasic"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS =
arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
Dependencies are same with codelab. Camera Preview works correctly but takePhoto does not. Has anyone else had this problem? , thank you.

Related

How to reduce image size captured with CameraX on Android?

I'm trying to understand how to use CameraX by studying this example:
class MainActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks {
private var imageCapture: ImageCapture? = null
private lateinit var outputDirectory: File
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestPermission()
// Set up the listener for take photo button
camera_capture_button.setOnClickListener { takePhoto() }
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun takePhoto() {
// Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
// Create time-stamped output file to hold the image
val photoFile = File(
outputDirectory,
SimpleDateFormat(
FILENAME_FORMAT, Locale.US
).format(System.currentTimeMillis()) + ".jpg"
)
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Set up image capture listener, which is triggered after photo has
// been taken
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
imageCapture = ImageCapture.Builder()
.build()
val imageAnalyzer = ImageAnalysis.Builder()
.build()
.also {
it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
Log.d(TAG, "Average luminosity: $luma")
})
}
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, imageAnalyzer
)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir != null && mediaDir.exists())
mediaDir else filesDir
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
private fun requestPermission() {
if (CameraUtility.hasCameraPermissions(this)) {
startCamera()
return
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
EasyPermissions.requestPermissions(
this,
"You need to accept the camera permission to use this app",
REQUEST_CODE_CAMERA_PERMISSION,
Manifest.permission.CAMERA
)
} else {
EasyPermissions.requestPermissions(
this,
"You need to accept the camera permission to use this app",
REQUEST_CODE_CAMERA_PERMISSION,
Manifest.permission.CAMERA
)
}
}
It works. The images captured with this code are rather big. On Pixel 3, it produces 4032x3024 JPEGs (usually around 4.5 to 6 MB). Does CameraX has a built-in feature to reduce the JPEG size?
I tried this:
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setTargetResolution(Size(800, 600))
.build()
and this:
val imageAnalyzer = ImageAnalysis.Builder()
.setTargetResolution(Size(800,600))
.build()
.also {
it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
})
}
Doesn't work. Still get the same 4032x3024 JPEG. I wonder if I don't understand the CameraX API properly.

Displaying an image from internal storage from android phone with Kotlin

I have made a very simple app to take a picture using camera x, with a viewfinder and it displays the image taken in the same activity
I am trying to get all the images taken from the internal storage and display them in another activity in the app with a recycler view
Im fine with recycler views, and I can use one with external Api's to display images and data but I just cannot figure out how to get the images from the internal storage, and add them to a list to display in the recycler view, Ive tried checking documentation but im getting nowhere, all I need is a simple code which will allow me to display the images as a thumbnail
class MainActivity : AppCompatActivity() {
private var imageCapture: ImageCapture? = null
private lateinit var outputDirectory: File
private lateinit var cameraExecutor: ExecutorService
private val TAG = "Snap"
private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private val REQUEST_CODE_PERMISSIONS = 10
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnSnap.setOnClickListener {
takePhoto()
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_GRANTED
) {
startCamera()
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA), REQUEST_CODE_PERMISSIONS
)
}
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor()
}
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>, grantResults: IntArray ) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_GRANTED) {
startCamera()
} else {
Toast.makeText(this, "Permissions not granted by the user",
Toast.LENGTH_SHORT).show()
finish()
}
}
private fun startCamera() {
val cameraProviderFuture = getInstance(this)
//add listener to the ProcessCameraProvider
cameraProviderFuture.addListener(Runnable {
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(viewFinder.createSurfaceProvider())}
imageCapture = ImageCapture.Builder().build()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
//unbind use cases before rebinding
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(this, cameraSelector, preview,
imageCapture)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun takePhoto() { val imageCapture = imageCapture ?: return
val photoFile = File(outputDirectory, SimpleDateFormat(FILENAME_FORMAT,
Locale.UK).format(System.currentTimeMillis()) + ".jpg")
val outputOpts = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(outputOpts,
ContextCompat.getMainExecutor(this), object :
ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
//save the photo to the file and display on screen
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
imgSnap.setImageURI(savedUri)
}
})
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir != null && mediaDir.exists()) mediaDir else filesDir
}
}
You can check out this link that provides you the better overview about using RecyclerView..
And in your bind view holder method in your adapter you can use this code to get the images from the storage and show it into your recyclerview as thumbnail.
String path = Environment.getExternalStorageDirectory() + "/myImage.jpg";
Bitmap bitmap = BitmapFactory.decodeFile(path);
You can learn more about the getting the images from storage Link.

how to get permission result in kotlin

I am trying to use a camera on my android. the user will be prompt for the permission of the camera. previously I used startActivityForResult and onRequestPermissionRequest for them. recently I found out that they are deprecated, so I'm trying out with registerForActivity. I managed to change to startActivity but I'm stuck at the permission request. I am wondering do I have to create another permissionlauncher or can I do the permission inside my resultlauncher.
companion object{
private const val CAMERA_PERMISSION_CODE = 1
private const val CAMERA_REQUEST_CODE = 2
}
val checkpermission = Manifest.permission.CAMERA
var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
val DP: Bitmap = data!!.extras!!.get("data") as Bitmap
val image = findViewById<ImageView>(R.id.imageButtonVerifyPhoto)
image.setImageBitmap(DP)
}
}
val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()){
isGranted ->
if(isGranted){
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
resultLauncher.launch((intent))
Toast.makeText(this,"Permission is tested", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(this,"Permission is denied",Toast.LENGTH_SHORT).show()
}
}
var cameraButton = findViewById<Button>(R.id.buttonRetakePhoto) // can change later
cameraButton.setOnClickListener {
if(ContextCompat.checkSelfPermission(
this,
checkpermission
) == PackageManager.PERMISSION_GRANTED
){
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
resultLauncher.launch(intent)
}else{
permissionLauncher.launch(checkpermission)
}
}
}
below is my previous code for the onRequestPermission
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode == CAMERA_REQUEST_CODE){
if(grantResults.isNotEmpty()&& grantResults[0] == PackageManager.PERMISSION_GRANTED){
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(intent, CAMERA_REQUEST_CODE)
}else{
Toast.makeText(this,"Permission is denied",Toast.LENGTH_SHORT).show()
}
}
}
Requesting runtime permissions is just a little more simplified
private val requestPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
// do something
}
Now we can call this to get any type of permission you want
cameraButton.setOnClickListener {
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
// Pass any permission you want while launching
requestPermission.launch(Manifest.permission.CAMERA)
}
}
Make sure to add in build.gradle
implementation 'androidx.fragment:fragment-ktx:1.2.0' // or later
implementation 'androidx.activity:activity-ktx:1.3.0' // or later
If you want to understand how all this works check here
Latest soluton in 2022: (no more request code)
Create Helper Extension Functions (for use in Fragment):
fun Fragment.requestPermissions(request: ActivityResultLauncher<Array<String>>, permissions: Array<String>) = request.launch(permissions)
fun Fragment.isAllPermissionsGranted(permissions: Array<String>) = permissions.all {
ContextCompat.checkSelfPermission(requireContext(), it) == PackageManager.PERMISSION_GRANTED
}
Request Permissions in Fragment:
class FirstFragment : Fragment() {
companion object {
private val PERMISSIONS = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
private lateinit var binding: FragmentFirstBinding
private lateinit var permissionsRequest: ActivityResultLauncher<Array<String>>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false)
permissionsRequest = getPermissionsRequest()
binding.grantButton.setOnClickListener {
requestPermissions(permissionsRequest, PERMISSIONS) //extension function
}
return binding.root
}
private fun getPermissionsRequest() = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
if (isAllPermissionsGranted(PERMISSIONS)) { //extension function
//do your stuff
} else {
//do your stuff
}
}
}
You could try Aaper, it allows to request for permissions using annotations like so:
Fist, add the permission to your manifest:
<uses-permission android:name="android.permission.CAMERA" />
Then, annotate a method that needs to use the camera:
#EnsurePermissions(permissions = [Manifest.permission.CAMERA])
fun takePhoto() {
Toast.makeText(this, "Camera permission granted", Toast.LENGTH_SHORT).show()
}
That's it, when you call the takePhoto method, it'll check for the camera permission, and if it's not available it will launch a permission request dialog. If the user approves it, then it will proceed to run takePhotos body.
Disclaimer, I'm the creator of Aaper

CameraX not able to capture a photo

I added the code from
https://developer.android.com/training/camerax
link
. The preview, permissions and everything else is working just fine, however I am not able to capture photo from the button. I declared the button to have a listener and connected the function takePhoto(), but it doesn't seem to work, in the past it works fine and I don't know if it's because of the new version of the CameraX feature is using, I don't know but it doesn't seem to be responding correctly. I have the Logcat open and when I press the button, it has the message that is pressed:
D/ViewRootImpl#70b5dc2[MainActivity]: ViewPostIme pointer 0
D/ViewRootImpl#70b5dc2[MainActivity]: ViewPostIme pointer 1
But that's it really, nothing else but that is my issue at the moment, it's a small issue but I believe I miss something to make this simple issue being resolved and I don't know what it causes to do so. Here is the codes that I will provide below:
Gradle:
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdk 30
defaultConfig {
applicationId "com.example.camerax1"
minSdk 21
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
implementation 'androidx.camera:camera-camera2:1.1.0-alpha08'
implementation 'androidx.camera:camera-lifecycle:1.1.0-alpha08'
implementation 'androidx.camera:camera-view:1.0.0-alpha28'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<androidx.camera.view.PreviewView
android:id="#+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="#+id/captureButton"
android:layout_width="85dp"
android:layout_height="85dp"
android:layout_marginBottom="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:importantForAccessibility="no" />
</androidx.constraintlayout.widget.ConstraintLayout>
Full code in once class MainActivity.kt
class MainActivity : AppCompatActivity() {
private var imageCapture: ImageCapture? = null
private lateinit var outputDirectory: File
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Request camera permissions
if (allPermissions()) {
startCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
val captureBtn = findViewById<Button>(R.id.captureButton)
captureBtn.setOnClickListener {
takePhoto()
}
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun takePhoto() {
Toast.makeText(baseContext, "Processing..", Toast.LENGTH_SHORT).show()
//Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
//Create time-stamped output file to hold the image
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.ENGLISH).format(System.currentTimeMillis()) + ".jpg")
//Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
//Set up image capture listener, which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
override fun onError(exception: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
}
}
)
}
private fun startCamera() {
val viewFiner = findViewById<PreviewView>(R.id.previewView)
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
//Used to bind the lifecycle of camera to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
//Preview
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(viewFiner.surfaceProvider)
}
//Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
//Unbind use cases before rebinding
cameraProvider.unbindAll()
//Bind use cases to camera
cameraProvider.bindToLifecycle(
this,
cameraSelector,
preview
)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
Toast.makeText(this, "Use case binding failed: $exc", Toast.LENGTH_SHORT).show()
}
}, ContextCompat.getMainExecutor(this))
}
private fun allPermissions() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissions()) {
startCamera()
} else {
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
finish()
}
}
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull().let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir.exists())
mediaDir else filesDir
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
companion object {
private const val TAG = "CameraXBasic"
private const val FILENAME_FORMAT = "dd.MM.yyyy - HH:mm:ss"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}
}
Your code lacks binding the ImageCapture.Builder() to the camera Provider which is the prominent reason why it is not capturing Images. You need to bind imageCapture builder , it can be done in the following way . In your startCamera() function , you need to bind imageCapture in the following way :
private fun startCamera() {
val viewFiner = findViewById<PreviewView>(R.id.previewView)
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
//Used to bind the lifecycle of camera to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
//Preview
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(viewFiner.surfaceProvider)
}
//set Image Capture Builder
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
//Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
//Unbind use cases before rebinding
cameraProvider.unbindAll()
//Bind use cases to camera
//Add imageCapture to lifecycle
cameraProvider.bindToLifecycle(
this,
cameraSelector,
preview,
imageCapture
)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
Toast.makeText(this, "Use case binding failed: $exc", Toast.LENGTH_SHORT).show()
}
}, ContextCompat.getMainExecutor(this))
}

Can I record video with CameraX (Android Jetpack)?

Google has released the new CameraX library as part of Jetpack. It looks great for taking pictures, but my use case also require making video's. I tried googling for that, but couldn't find anything.
So, is it possible to record videos with the CameraX Jetpack library?
Yes, we can record video using CameraX. I have tried to implement myself with help of Github demo for CameraX. Please refer below code may it helps you.
Config for Video in CameraX:
val videoCaptureConfig = VideoCaptureConfig.Builder().apply {
setLensFacing(lensFacing)
setTargetAspectRatio(screenAspectRatio)
setTargetRotation(viewFinder.display.rotation)
}.build()
videoCapture = VideoCapture(videoCaptureConfig)
CameraX.bindToLifecycle(this, preview, imageCapture, videoCapture)
To Start video recording:
videoCapture?.startRecording(videoFile, object : VideoCapture.OnVideoSavedListener {
override fun onVideoSaved(file: File?) {
Log.i(javaClass.simpleName, "Video File : $file")
}
override fun onError(useCaseError: VideoCapture.UseCaseError?, message: String?, cause: Throwable?) {
Log.i(javaClass.simpleName, "Video Error: $message")
}
})
To Stop video recording:
videoCapture?.stopRecording()
Same above I have mentioned in Github issue comment: https://github.com/android/camera/issues/2#issuecomment-490773932
Notes: There may be different in code to implementation of video recording using CameraX. Because this above code was developed by me without any other reference rather than Github Demo.
Please check important comment of Oscar Wahltinez on this answer as of 14 May 2019
This is my solution
//Versions in Gradle
def camerax_version = "1.0.0-beta06"
def camera_extensions = "1.0.0-alpha13"
private lateinit var videoCapture: VideoCapture
private lateinit var viewFinder: PreviewView
private lateinit var outputDirectory: File
private var lensFacing: Int = CameraSelector.LENS_FACING_FRONT
private val executor = Executors.newSingleThreadExecutor()
private var isRecording = false
private var camera: Camera? = null
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
//onCreate
viewFinder = preview_video_view
runWithPermissions(*permissions) {
startCamera(view.context)
initClicks()
}
#SuppressLint("RestrictedApi", "UnsafeExperimentalUsageError")
private fun startCamera(context: Context) {
outputDirectory = getOutputDirectory(context)
cameraProviderFuture = ProcessCameraProvider.getInstance(context)
val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
// Create a configuration object for the video use case
val videoCaptureConfig = VideoCaptureConfig.Builder().apply {
setTargetRotation(viewFinder.display.rotation)
setCameraSelector(cameraSelector)
}
//CameraX.initialize(context, this.cameraXConfig)
videoCapture = VideoCapture(videoCaptureConfig.useCaseConfig)
val preview: Preview = Preview.Builder().apply {
setTargetAspectRatio(AspectRatio.RATIO_16_9)
setTargetRotation(viewFinder.display.rotation)
}.build()
preview.setSurfaceProvider(viewFinder.createSurfaceProvider())
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
camera = cameraProvider.bindToLifecycle(
viewLifecycleOwner,
cameraSelector,
preview,
videoCapture
)
}, ContextCompat.getMainExecutor(context))
}
#SuppressLint("RestrictedApi")
private fun startRecording() {
val file = createFile(
outputDirectory,
FILENAME,
VIDEO_EXTENSION
)
videoCapture.startRecording(
file,
executor,
object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(file: File) {
Handler(Looper.getMainLooper()).post {
showMessage(file.name + " is saved")
}
}
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
Handler(Looper.getMainLooper()).post {
showMessage(videoCaptureError.toString() + " " + message)
}
}
}
)
}
#SuppressLint("RestrictedApi")
private fun stopRecording() {
videoCapture.stopRecording()
}
override fun getCameraXConfig(): CameraXConfig {
return Camera2Config.defaultConfig()
}
companion object {
private const val FILENAME = "yyyy_MM_dd_HH_mm_ss"
private const val VIDEO_EXTENSION = ".mp4"
private val permissions = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
fun getOutputDirectory(context: Context): File {
val appContext = context.applicationContext
val mediaDir = appContext.externalMediaDirs.firstOrNull()?.let {
File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir != null && mediaDir.exists()) mediaDir else appContext.filesDir
}
fun createFile(baseFolder: File, format: String, extension: String) =
File(baseFolder, SimpleDateFormat(format, Locale.US)
.format(System.currentTimeMillis()) + extension)
}
Updating on Patel Pinkal's answer.
Having beta released, we can't use VideoCaptureConfig.Builder() anymore, instead you go with something like this:
videoCapture = VideoCapture.Builder().apply {
// init config here
}.build()
As of April 2021
val videoCapture = VideoCapture.Builder().build()
val outputDirectory = getOutputDirectory()
fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() } }
return if (mediaDir != null && mediaDir.exists())
mediaDir else filesDir
}
#SuppressLint("RestrictedApi")
private fun startRecording() {
val videoFile = File(
outputDirectory,
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US
).format(System.currentTimeMillis()) + ".mp4")
val outputOptions = VideoCapture.OutputFileOptions.Builder(videoFile).build()
videoCapture?.startRecording(outputOptions, ContextCompat.getMainExecutor(this), object: VideoCapture.OnVideoSavedCallback {
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
Log.e(TAG, "Video capture failed: $message")
}
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
val savedUri = Uri.fromFile(videoFile)
val msg = "Video capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
#SuppressLint("RestrictedApi")
private fun stopRecording() {
videoCapture?.stopRecording()
}
Please note that this API is still restricted and is still subject to change.
CameraX VideoCapture use case is implemented in camera-video, released since 1.1.0-alpha30. Refer to the following:
the official documentation
the reference doc
sample
youtube video(current CameraX status)
for details.
Regarding Sergei's answer, videoCapture.startRecording() recieves VideoCapture.OutputFileOptions instead of file for camerax_version = '1.0.0-rc01', so it should be used as:
videoCapture.startRecording(
VideoCapture.OutputFileOptions.Builder(file).build(),
executor,
object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
TODO("Not yet implemented")
}
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
TODO("Not yet implemented")
}
}

Categories

Resources