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
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!