Control commit

This commit is contained in:
Captain Arepa 2024-05-28 09:36:36 -04:00
parent 623c1bf95f
commit 01bc596284
3 changed files with 151 additions and 137 deletions

View file

@ -43,7 +43,6 @@ public class CameraActivityNew extends AppCompatActivity implements ActivityComp
mTextureView = binding.inCamera2.tvCameraTextureView;
mSegpassCamera = new SegpassCamera(this, mTextureView, this, this);
mSegpassCamera.init();
setUpListeners();
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {

View file

@ -33,9 +33,11 @@ import android.view.TextureView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.example.cameraxtestappjava.segpass.camera.exceptions.SegpassCameraException;
import com.example.cameraxtestappjava.segpass.camera.utils.CompareSizesByArea;
import com.example.cameraxtestappjava.segpass.camera.utils.ImageSaver;
import com.example.cameraxtestappjava.segpass.camera.utils.ImageEncodingCallback;
@ -88,6 +90,11 @@ public class SegpassCamera {
*/
private static final int MAX_PREVIEW_HEIGHT = 1080;
/**
* Error constants
*/
private static final String ERROR_CAMERA_ACCESS = "Camera access error.";
/**
* ID of the current {@link CameraDevice}.
*/
@ -128,11 +135,6 @@ public class SegpassCamera {
*/
private ImageReader mImageReader;
/**
* This is the output file for our picture.
*/
private File mFile;
/**
* This is the folder where the pictures will be saved.
*/
@ -143,6 +145,12 @@ public class SegpassCamera {
*/
private String mBase64Value;
private final CaptureRequest.Key<Integer> mCaptureAfModeKey = CaptureRequest.CONTROL_AF_MODE;
private final int mCaptureAfModeValue = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
private final CaptureRequest.Key<Integer> mCaptureAfTriggerKey = CaptureRequest.CONTROL_AF_TRIGGER;
private final int mCaptureAfTriggervalue = CaptureRequest.CONTROL_AF_TRIGGER_START;
/**
* {@link CaptureRequest.Builder} for the camera preview
*/
@ -164,7 +172,6 @@ public class SegpassCamera {
private int mSensorOrientation;
/**
*
* @param activity Parent activity where the camera is called from.
* @param textureView {@link AutoFitTextureView} where the camera preview is shown.
* @param listener {@link SegpassPermissionListener} that deals with camera and storage permissions.
@ -177,13 +184,6 @@ public class SegpassCamera {
mCameraCallback = cameraCallback;
}
/**
* Method to initialize camera components.
*/
public void init() {
createFolder();
}
/**
* {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
* {@link TextureView}.
@ -208,6 +208,7 @@ public class SegpassCamera {
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
// No code implementation needed for this use case.
}
};
@ -264,17 +265,11 @@ public class SegpassCamera {
* This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
* still image is ready to be saved.
*/
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = reader -> {
// Set the capture image
Image mImage = reader.acquireNextImage();
// Post the image to the thread so it can be converted to base64
mBackgroundHandler.post(new ImageSaver(mImage, base64Callback));
}
};
/**
@ -297,6 +292,7 @@ public class SegpassCamera {
mBackgroundHandler = null;
} catch (InterruptedException e) {
Log.d(TAG, "InterruptedException@stopBackgroundThread(): " + e.getMessage());
mBackgroundThread.interrupt();
}
}
@ -397,27 +393,35 @@ public class SegpassCamera {
CameraCharacteristics characteristics
= manager.getCameraCharacteristics(cameraId);
// We don't use a back facing camera.
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
continue;
}
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
StreamConfigurationMap map = getStreamConfigurationMap(characteristics);
if (map == null) continue;
// For still image captures, we use the largest available size.
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
ImageFormat.JPEG, /*maxImages*/2);
mImageReader.setOnImageAvailableListener(
mOnImageAvailableListener, mBackgroundHandler);
setImageReaderListeners(largest);
boolean swappedDimensions = isSwappedDimensions(characteristics);
getOptimalPreviewSize(width, height, swappedDimensions, map, largest);
setPreviewAspectRatio();
mCameraId = cameraId;
return;
}
} catch (CameraAccessException e) {
Log.e(TAG, "CameraAccessException@setUpCameraOutputs(): " + e.getMessage());
mCameraCallback.onCameraInitError(e.getMessage());
} catch (NullPointerException e) {
Log.e(TAG, "NullPointerException@setUpCameraOutputs(): " + e.getMessage());
mCameraCallback.onCameraInitError(e.getMessage());
}
}
private boolean isSwappedDimensions(CameraCharacteristics characteristics) {
// Find out if we need to swap dimension to get the preview size relative to sensor
// coordinate.
int displayRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
@ -440,7 +444,23 @@ public class SegpassCamera {
default:
Log.e(TAG, "Display rotation is invalid: " + displayRotation);
}
return swappedDimensions;
}
@Nullable
private static StreamConfigurationMap getStreamConfigurationMap(CameraCharacteristics characteristics) {
// We don't use a back facing camera.
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
// If facis is null, return
if (facing == null) return null;
if (facing == CameraCharacteristics.LENS_FACING_BACK) return null;
return characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
}
private void getOptimalPreviewSize(int width, int height, boolean swappedDimensions, StreamConfigurationMap map, Size largest) {
Point displaySize = new Point();
mActivity.getWindowManager().getDefaultDisplay().getSize(displaySize);
int rotatedPreviewWidth = width;
@ -469,7 +489,16 @@ public class SegpassCamera {
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
maxPreviewHeight, largest);
}
private void setImageReaderListeners(Size largest) {
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
ImageFormat.JPEG, /*maxImages*/2);
mImageReader.setOnImageAvailableListener(
mOnImageAvailableListener, mBackgroundHandler);
}
private void setPreviewAspectRatio() {
// We fit the aspect ratio of TextureView to the size of preview we picked.
int orientation = mActivity.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
@ -479,17 +508,6 @@ public class SegpassCamera {
mTextureView.setAspectRatio(
mPreviewSize.getHeight(), mPreviewSize.getWidth());
}
mCameraId = cameraId;
return;
}
} catch (CameraAccessException e) {
Log.e(TAG, "CameraAccessException@setUpCameraOutputs(): " + e.getMessage());
mCameraCallback.onCameraInitError(e.getMessage());
} catch (NullPointerException e) {
Log.e(TAG, "NullPointerException@setUpCameraOutputs(): " + e.getMessage());
mCameraCallback.onCameraInitError(e.getMessage());
}
}
/**
@ -507,7 +525,7 @@ public class SegpassCamera {
CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
throw new SegpassCameraException("Time out waiting to lock camera opening.");
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
@ -515,7 +533,7 @@ public class SegpassCamera {
mCameraCallback.onCameraInitError(e.getMessage());
} catch (InterruptedException e) {
Log.e(TAG, "InterruptedException@openCamera(): " + e.getMessage());
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
throw new SegpassCameraException("Interrupted while trying to lock camera opening.", e);
}
}
@ -538,7 +556,7 @@ public class SegpassCamera {
mImageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
throw new SegpassCameraException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
@ -578,11 +596,9 @@ public class SegpassCamera {
mCaptureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mPreviewRequestBuilder.set(mCaptureAfModeKey, mCaptureAfModeValue);
// Trigger AF
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
mPreviewRequestBuilder.set(mCaptureAfTriggerKey, mCaptureAfTriggervalue);
// Finally, we start displaying the camera preview.
mPreviewRequest = mPreviewRequestBuilder.build();
@ -602,8 +618,8 @@ public class SegpassCamera {
}, null
);
} catch (CameraAccessException e) {
Log.e(TAG, "createCameraPreviewSession(): " + e. getMessage());
mCameraCallback.onCameraInitError("Camera access error.");
Log.e(TAG, "createCameraPreviewSession(): " + e.getMessage());
mCameraCallback.onCameraInitError(ERROR_CAMERA_ACCESS);
}
}
@ -632,7 +648,7 @@ public class SegpassCamera {
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
matrix.postRotate(90 * (rotation - 2L), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
@ -706,8 +722,8 @@ public class SegpassCamera {
try {
session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "onConfigured@takePicture(): " + e. getMessage());
mCameraCallback.onCameraInitError("Camera access error.");
Log.e(TAG, "onConfigured@takePicture(): " + e.getMessage());
mCameraCallback.onCameraInitError(ERROR_CAMERA_ACCESS);
}
}
@ -716,9 +732,10 @@ public class SegpassCamera {
}
}, mBackgroundHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "takePicture(): " + e. getMessage());
mCameraCallback.onCameraInitError("Camera access error.");
Log.e(TAG, "takePicture(): " + e.getMessage());
mCameraCallback.onCameraInitError(ERROR_CAMERA_ACCESS);
}
}
private int getOrientation(int rotation) {
@ -728,26 +745,4 @@ public class SegpassCamera {
// For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
}
/**
* Creates the custom folder {@link SegpassCamera#mFileFolder} where pictures are saved.
*/
public void createFolder() {
PackageManager pm = mActivity.getPackageManager();
String folderName;
try {
ApplicationInfo ai = pm.getApplicationInfo(mActivity.getPackageName(), 0);
folderName = pm.getApplicationLabel(ai).toString();
} catch (Exception e) {
Log.e(TAG, "Can't find the package name.");
folderName = "CustomCameraLibraryFolder";
}
File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
mFileFolder = new File(folder, folderName);
if (!mFileFolder.exists()) {
mFileFolder.mkdirs();
}
}
}

View file

@ -0,0 +1,20 @@
package com.example.cameraxtestappjava.segpass.camera.exceptions;
public class SegpassCameraException extends RuntimeException {
// Optional: You can define a constructor with a message argument
public SegpassCameraException(String message) {
super(message);
}
// Optional: You can define a constructor with a message and a cause (throwable)
public SegpassCameraException(String message, Throwable cause) {
super(message, cause);
}
// Optional: You can define custom methods specific to your exception
public String getCustomDetails() {
// ... Add logic to return additional details related to the exception
return "This is a custom detail";
}
}