Reputation: 19
I am new to Android App development. I read https://github.com/Pinkal7600/camera-samples/blob/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt for better understanding how to write the zoom function.
However, my program just didn't work. I am thinking about if it is related to the value I changed, which means I need to return the setLinearZoom. Thank you for reading!
Below are my code:
import android.Manifest
import android.content.ContentValues
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.widget.SeekBar
import android.widget.Toast
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.Recorder
import androidx.camera.video.Recording
import androidx.camera.video.VideoCapture
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.mycamerax.databinding.ActivityMainBinding
import com.google.common.util.concurrent.ListenableFuture
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
typealias LumaListener = (luma: Double) -> Unit //typealias: nick name some datatypes
//(Double)-> Unit means input double and output nothing
class MainActivity : AppCompatActivity() {
//viewBinding can help with the multi-widgets
private lateinit var viewBinding: ActivityMainBinding
private var imageCapture: ImageCapture? = null
private var videCpature: VideoCapture<Recorder>? = null
private var recording: Recording? = null
private var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
// no need! private var preview: Preview? = null //預覽物件
// private var cameraProvider: ProcessCameraProvider? = null//相機資訊
// private var camera: Camera? = null//相機物件
private lateinit var cameraExecutor: ExecutorService
companion object { // same thing as static object
private const val TAG = "CameraXAPP"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CODE_PERMISSIONS = 10 // here is the definition
private val REQUIRED_PERMISSIONS =
mutableListOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
).apply {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P){
add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}.toTypedArray()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
//Request camera permissions
if(allPermissionsGranted()){
startCamera()
}else{
ActivityCompat.requestPermissions(
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
)
}
//set up the listeners for take photo and video capture buttons
viewBinding.imageCaptureButton.setOnClickListener { takePhoto() }
viewBinding.videoCaptureButton.setOnClickListener { captureVideo() }
//use single thread to execute my program
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun takePhoto() {
//Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture?:return// if the camera hasn't been set up, it will be null. return 0, finish the program.
//create time stamped name and MediaStore entry.
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US)
.format(System.currentTimeMillis())
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)//use MediaStore to store ImageCapture
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
}
}
//Create output options object which contains file + metadata
//we want to save in MediaStore so that other apps can display
val outputOptions = ImageCapture.OutputFileOptions.Builder(
contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues
).build()
//Set up image capture listener, which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object: ImageCapture.OnImageSavedCallback{
//if capture or save failed
override fun onError(exc: ImageCaptureException){
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
//succeeded
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
// val savedUri = Uri.fromFile(photoFile)
val msg = "Photo capture succeeded: ${output.savedUri}"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
}
)
}
private fun updateCameraUi(){
val cameraSelector = cameraSelector
val preview = Preview.Builder().build().also{
it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)
}
val cameraProvider = cameraProviderFuture.get()
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
val cameraControl = camera.cameraControl
val cameraInfo = camera.cameraInfo
val currentZoomRatio = cameraInfo.zoomState.value?.zoomRatio
viewBinding.zoomSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar?,
progress: Int,
fromUser: Boolean
) {
cameraControl.setLinearZoom(progress / 100.toFloat())
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
}
private fun startCamera() {
//create the instance for binding the lifecycle of camera to the lifecycle owner
// val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
//returns the Executor for two arguments
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()//bind the lifecycle of our camera to the Lifecycle owner
//Preview
//initialize our Preview
val preview = Preview.Builder().build().also{
it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)
}
//image capture
imageCapture = ImageCapture.Builder().build()
//Create a camera selector and select back camera
val cameraSelector = cameraSelector
updateCameraUi()
try{
cameraProvider.unbindAll()//unbind use cases before rebuilding
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
}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
}
// Check if the request code is correct; ignore it otherwise.
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {//If the permissions are granted,
startCamera()
} else {//If permissions are not granted, present a toast to notify the user that the permissions were not granted.
Toast.makeText(this,
"Permissions not granted by the user.",
Toast.LENGTH_SHORT).show()
finish()
}
}
}
private fun captureVideo() {}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
}
And this is the XML file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="-56dp"
tools:layout_editor_absoluteY="-36dp" />
<Button
android:id="@+id/image_capture_button"
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_marginBottom="50dp"
android:layout_marginEnd="50dp"
android:elevation="2dp"
android:text="@string/take_photo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toStartOf="@id/vertical_centerline" />
<Button
android:id="@+id/video_capture_button"
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_marginBottom="50dp"
android:layout_marginStart="50dp"
android:elevation="2dp"
android:text="@string/start_capture"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/vertical_centerline" />
<SeekBar
android:id="@+id/zoom_Seek_Bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:thumb="@drawable/ic_action_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_centerline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".50" />
</androidx.constraintlayout.widget.ConstraintLayout>
Upvotes: 0
Views: 1278
Reputation: 11
I tried running your program and it works on my side! I used CameraX version 1.1.0-rc02
and tested it on a Pixel 4a.
Try updating CameraX to a more recent version and if the problem still persist, that may indicate a compatibility issue with your device and you should report it to CameraX's issue tracker
Upvotes: 1