Search code examples
javaandroidandroid-cameraandroid-camerax

CameraX using Java (android studio)


Here is an image screenshot App screenshot Can anyone help me with a working repo or link to a working version of camera x with use case selector preview and image capture?

I’ve traced the problem to the image capture and I think it has something to do with the executor (not sure) I’ve tried ContextCompat.getMainExecutor(this) and Executors.newSingleThreadExecutor() as arguments in imageCapture.takePicture

The selector works fine, but I can’t seem to get the image capture to work I have included my code(Gradle, Manifest, XML, MainActivity)

BuidGradle

plugins {
id 'com.android.application'
}

android {
 compileSdk 32

defaultConfig {
    applicationId "com.example.camerax"
    minSdk 21
    targetSdk 32
    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
}
}

dependencies {

implementation 'com.github.bumptech.glide:glide:4.13.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2'

implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 
'androidx.test.espresso:espresso-core:3.4.0'



def camerax_version = "1.1.0-beta01"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation 
"androidx.camera:camera-camera2:${camerax_version}"
implementation 
"androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-video:${camerax_version}"

implementation "androidx.camera:camera-view:${camerax_version}"
implementation 
"androidx.camera:camera-extensions:${camerax_version}"}

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.camerax">

<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission 
 android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission 
 android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
    android:allowBackup="true"
    android:dataExtractionRules="@xml/data_extraction_rules"
    android:fullBackupContent="@xml/backup_rules"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.CameraX"
    tools:targetApi="31">
    <activity
        android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category 
        android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
       </activity>
      </application>

   </manifest>

XML...

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FF000000"
    android:orientation="vertical">

    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:contentDescription="preview_area" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:gravity="center"
        android:orientation="horizontal"
        android:padding="16dp">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:src="@color/white"
            android:scaleType="centerCrop"/>


        <ImageButton
            android:id="@+id/camera_btn"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:layout_marginStart="64dp"
            android:layout_marginEnd="64dp"
            android:background="@drawable/round_button"
            android:src="@drawable/ic_baseline_photo_camera_24"/>

        <ImageButton
            android:id="@+id/switch_camera_btn"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background=
            "@drawable/ic_baseline_flip_camera_android_24"/>


    </LinearLayout>
 
</LinearLayout> 

MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

boolean defCamera;
PreviewView previewView;
ImageView imageHolder;
Preview preview;
ImageButton capture, switchCamera;
CameraSelector cameraSelector;
ProcessCameraProvider cameraProvider;
private final int REQUEST_CODE_PERMISSIONS = 100;
private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA","android.permission.WRITE_EXTERNAL_STORAGE"};
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private ImageCapture imageCapture;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    defCamera = true;

    previewView = findViewById(R.id.previewView);
    imageHolder = findViewById(R.id.imageView);
    capture = findViewById(R.id.camera_btn);
    switchCamera = findViewById(R.id.switch_camera_btn);

    imageHolder.setOnClickListener(this);
    capture.setOnClickListener(this);
    switchCamera.setOnClickListener(this);

    if (allPermissionsGranted()) {
        startCameraX();
    } else {
        ActivityCompat.requestPermissions(this,
                REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
    }

}// end of onCreate


private void startCameraX() {
    cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    cameraProviderFuture.addListener(new Runnable() {
        @Override
        public void run() {
            try {
                cameraProvider = cameraProviderFuture.get();
                bindPreview(cameraProvider);

            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, ContextCompat.getMainExecutor(this));


}

void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {

    cameraProvider.unbindAll();


    //Selector Use case
    if (defCamera) {
        cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();

    } else {
        cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                .build();
    }


    //Preview Use case
    preview = new Preview.Builder().build();
    preview.setSurfaceProvider(previewView.getSurfaceProvider());

    //ImageCapture use case
    imageCapture = new ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .setTargetRotation(this.getWindowManager().getDefaultDisplay().getRotation())
            .build();

    cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);
}


@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.imageView:
            // do nothing for now

            break;

        case R.id.camera_btn:
            capturePhoto();
            break;


        case R.id.switch_camera_btn:
            defCamera = !defCamera;
            startCameraX();
            break;
    }

}

private void capturePhoto() {


    SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
    File file = new File(getBatchDirectoryName(), mDateFormat.format(new Date())+ ".jpg");

    ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();


    imageCapture.takePicture(outputFileOptions,
            ContextCompat.getMainExecutor(this),
            new ImageCapture.OnImageSavedCallback() {
                @Override
                public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {

                    Uri picUri = outputFileResults.getSavedUri();
                    if (picUri == null){
                        Toast.makeText(MainActivity.this, "Uri is Empty, Image might be saved", Toast.LENGTH_SHORT).show();
                    }else
                        Glide.with(getBaseContext())
                                .load(picUri)
                                .into(imageHolder);
                        Toast.makeText(MainActivity.this, "Image saved at " + picUri.getPath() + " Uri is not Empty, saved", Toast.LENGTH_SHORT).show();

                        }


                @Override
                public void onError(@NonNull ImageCaptureException exception) {
                    exception.printStackTrace();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "Image Capture error", Toast.LENGTH_SHORT).show();
                        }
                    });

                }
            });

 }

public String getBatchDirectoryName() {

    String app_folder_path = "";
    app_folder_path = Environment.getExternalStorageDirectory().toString() + "/CameraX";
    File dir = new File(app_folder_path);
    if (!dir.exists() && !dir.mkdirs()) {

    }

    return app_folder_path;
}




@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    //start camera when permissions have been granted otherwise exit app
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CODE_PERMISSIONS) {
        if (allPermissionsGranted()) {
            startCameraX();
        } else {
            Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
}

private boolean allPermissionsGranted(){
    //check if req permissions have been granted
    for(String permission : REQUIRED_PERMISSIONS){
        if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
            return false;
        }
    }
    return true;
  }
}

Log

2022-08-13 15:50:02.927 528-1628/system_process I/system_server: 
oneway function results will be dropped but finished with status OK 

and parcel size 4
2022-08-13 15:50:02.928 528-1628/system_process I/chatty: uid=1000(system) Binder:528_D identical 1 line
2022-08-13 15:50:02.929 528-1628/system_process I/system_server: oneway function results will be dropped but finished with status OK and parcel size 4
2022-08-13 15:50:02.929 1335-1494/com.android.providers.media.module E/MediaProvider: Creating a non-default top level directory or deleting an existing one is not allowed!
2022-08-13 15:50:02.931 1335-1494/com.android.providers.media.module E/MediaProvider: Creating a non-default top level directory or deleting an existing one is not allowed!
2022-08-13 15:50:02.932 5770-5770/com.example.camerax D/CameraOrientationUtil: getRelativeImageRotation: destRotationDegrees=0, sourceRotationDegrees=90, isOppositeFacing=true, result=90
2022-08-13 15:50:02.932 5770-5770/com.example.camerax D/CameraOrientationUtil: getRelativeImageRotation: destRotationDegrees=0, sourceRotationDegrees=90, isOppositeFacing=true, result=90
2022-08-13 15:50:02.932 5770-5770/com.example.camerax D/ImageCapture: Send image capture request [current, pending] = [0, 1]
2022-08-13 15:50:02.932 5770-5770/com.example.camerax D/ImageCapture: issueTakePicture
2022-08-13 15:50:02.935 5770-5797/com.example.camerax D/Camera2CameraImpl: {Camera@77a318b[id=0]} Issue capture request
2022-08-13 15:50:02.935 5770-5797/com.example.camerax D/CaptureSession: Issuing capture request.
2022-08-13 15:50:03.063 528-1628/system_process I/system_server: oneway function results will be dropped but finished with status OK and parcel size 4
2022-08-13 15:50:03.132 304-356/? D/goldfish-address-space: claimShared: Ask to claim region [0x3e41c2000 0x3e4384000]
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err: androidx.camera.core.ImageCaptureException: Failed to write temp file
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at androidx.camera.core.ImageCapture$3.onError(ImageCapture.java:948)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at androidx.camera.core.ImageSaver.lambda$postError$2$androidx-camera-core-ImageSaver(ImageSaver.java:342)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at androidx.camera.core.ImageSaver$$ExternalSyntheticLambda1.run(Unknown Source:8)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at android.os.Handler.handleCallback(Handler.java:938)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at android.os.Looper.loop(Looper.java:223)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:7656)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err: Caused by: java.io.FileNotFoundException: /storage/emulated/0/CameraX/CameraXe15a1fb9-1e86-4cef-a1b7-d9a0fac63368.tmp: open failed: ENOENT (No such file or directory)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:492)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:236)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:186)
2022-08-13 15:50:03.189 5770-5770/com.example.camerax W/System.err:     at androidx.camera.core.ImageSaver.saveImageToTempFile(ImageSaver.java:128)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at androidx.camera.core.ImageSaver.run(ImageSaver.java:95)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at java.lang.Thread.run(Thread.java:923)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err: Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at libcore.io.Linux.open(Native Method)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:254)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7542)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:478)
2022-08-13 15:50:03.191 5770-5770/com.example.camerax W/System.err:     ... 7 more
2022-08-13 15:50:03.214 304-356/? D/goldfish-address-space: claimShared: Ask to claim region [0x3e7e42000 0x3e7e82000]
2022-08-13 15:50:03.222 304-356/? D/goldfish-address-space: claimShared: Ask to claim region [0x3e7e82000 0x3e7ec2000]
2022-08-13 15:50:03.225 304-356/? D/goldfish-address-space: claimShared: Ask to claim region [0x3e7ec2000 0x3e7f02000]
2022-08-13 15:50:05.211 528-599/system_process W/NotificationService: Toast already killed. pkg=com.example.camerax token=android.os.BinderProxy@ff187ea
2022-08-13 15:50:06.021 528-1628/system_process I/system_server: oneway function results will be dropped but finished with status OK and parcel size 4
2022-08-13 15:50:06.699 528-548/system_process W/WindowManager: Unable to start animation, surface is null or no children.
2022-08-13 15:50:08.297 1187-6041/com.google.android.gms E/WakeLock: GCM_HB_ALARM release without a matched acquire!
2022-08-13 15:50:08.302 207-606/? E/[email protected]: Error opening kernel wakelock stats for: wakeup34: Permission denied
2022-08-13 15:50:08.305 207-606/? E/[email protected]: Error opening kernel wakelock stats for: wakeup35: Permission denied
2022-08-13 15:50:08.296 207-207/? W/Binder:207_3: type=1400 audit(0.0:435): avc: denied { read } for name="wakeup34" dev="sysfs" ino=18950 scontext=u:r:system_suspend:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
2022-08-13 15:50:08.300 207-207/? W/Binder:207_3: type=1400 audit(0.0:436): avc: denied { read } for name="wakeup35" dev="sysfs" ino=19013 scontext=u:r:system_suspend:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
2022-08-13 15:50:16.209 528-528/system_process W/WindowManager: removeWindowToken: Attempted to remove non-existing token: android.os.Binder@def20c7

Solution

  • Now we know that the error is caused by the outputFileOptions. I look into the codelab on how to do it and it looks different. As the code is in Kotlin, I will share it here in Java.

    String name = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US).format(new Date());
    ContentValues contentValues = ContentValues();
    contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
    if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
        contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image");
    }
    
    // Create output options object which contains file + metadata
    ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions
        .Builder(context.contentResolver,
                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                 contentValues).build();
    

    Hope it solves the problem!