mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-23 16:37:30 +00:00
260 lines
8.0 KiB
Java
260 lines
8.0 KiB
Java
package org.thoughtcrime.securesms.mediasend;
|
|
|
|
import android.graphics.SurfaceTexture;
|
|
import android.hardware.Camera;
|
|
import androidx.annotation.NonNull;
|
|
import android.view.Surface;
|
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
|
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
|
|
class Camera1Controller {
|
|
|
|
private static final String TAG = Camera1Controller.class.getSimpleName();
|
|
|
|
private final int screenWidth;
|
|
private final int screenHeight;
|
|
private final OrderEnforcer<Stage> enforcer;
|
|
private final EventListener eventListener;
|
|
|
|
private Camera camera;
|
|
private int cameraId;
|
|
private SurfaceTexture previewSurface;
|
|
private int screenRotation;
|
|
|
|
Camera1Controller(int preferredDirection, int screenWidth, int screenHeight, @NonNull EventListener eventListener) {
|
|
this.eventListener = eventListener;
|
|
this.enforcer = new OrderEnforcer<>(Stage.INITIALIZED, Stage.PREVIEW_STARTED);
|
|
this.cameraId = Camera.getNumberOfCameras() > 1 ? preferredDirection : Camera.CameraInfo.CAMERA_FACING_BACK;
|
|
this.screenWidth = screenWidth;
|
|
this.screenHeight = screenHeight;
|
|
}
|
|
|
|
void initialize() {
|
|
Log.d(TAG, "initialize()");
|
|
|
|
if (Camera.getNumberOfCameras() <= 0) {
|
|
Log.w(TAG, "Device doesn't have any cameras.");
|
|
onCameraUnavailable();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
camera = Camera.open(cameraId);
|
|
} catch (Exception e) {
|
|
Log.w(TAG, "Failed to open camera.", e);
|
|
onCameraUnavailable();
|
|
return;
|
|
}
|
|
|
|
if (camera == null) {
|
|
Log.w(TAG, "Null camera instance.");
|
|
onCameraUnavailable();
|
|
return;
|
|
}
|
|
|
|
Camera.Parameters params = camera.getParameters();
|
|
Camera.Size previewSize = getClosestSize(camera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight);
|
|
Camera.Size pictureSize = getClosestSize(camera.getParameters().getSupportedPictureSizes(), screenWidth, screenHeight);
|
|
final List<String> focusModes = params.getSupportedFocusModes();
|
|
|
|
Log.d(TAG, "Preview size: " + previewSize.width + "x" + previewSize.height + " Picture size: " + pictureSize.width + "x" + pictureSize.height);
|
|
|
|
params.setPreviewSize(previewSize.width, previewSize.height);
|
|
params.setPictureSize(pictureSize.width, pictureSize.height);
|
|
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
|
|
params.setColorEffect(Camera.Parameters.EFFECT_NONE);
|
|
params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
|
|
|
|
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
|
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
|
} else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
|
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
|
}
|
|
|
|
|
|
camera.setParameters(params);
|
|
|
|
enforcer.markCompleted(Stage.INITIALIZED);
|
|
|
|
eventListener.onPropertiesAvailable(getProperties());
|
|
}
|
|
|
|
void release() {
|
|
Log.d(TAG, "release() called");
|
|
enforcer.run(Stage.INITIALIZED, () -> {
|
|
Log.d(TAG, "release() executing");
|
|
previewSurface = null;
|
|
camera.stopPreview();
|
|
camera.release();
|
|
enforcer.reset();
|
|
});
|
|
}
|
|
|
|
void linkSurface(@NonNull SurfaceTexture surfaceTexture) {
|
|
Log.d(TAG, "linkSurface() called");
|
|
enforcer.run(Stage.INITIALIZED, () -> {
|
|
try {
|
|
Log.d(TAG, "linkSurface() executing");
|
|
previewSurface = surfaceTexture;
|
|
|
|
camera.setPreviewTexture(surfaceTexture);
|
|
camera.startPreview();
|
|
enforcer.markCompleted(Stage.PREVIEW_STARTED);
|
|
} catch (Exception e) {
|
|
Log.w(TAG, "Failed to start preview.", e);
|
|
eventListener.onCameraUnavailable();
|
|
}
|
|
});
|
|
}
|
|
|
|
void capture(@NonNull CaptureCallback callback) {
|
|
enforcer.run(Stage.PREVIEW_STARTED, () -> {
|
|
camera.takePicture(null, null, null, (data, camera) -> {
|
|
callback.onCaptureAvailable(data, cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT);
|
|
});
|
|
});
|
|
}
|
|
|
|
int flip() {
|
|
Log.d(TAG, "flip()");
|
|
SurfaceTexture surfaceTexture = previewSurface;
|
|
cameraId = (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) ? Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
|
|
|
|
release();
|
|
initialize();
|
|
linkSurface(surfaceTexture);
|
|
setScreenRotation(screenRotation);
|
|
|
|
return cameraId;
|
|
}
|
|
|
|
void setScreenRotation(int screenRotation) {
|
|
Log.d(TAG, "setScreenRotation(" + screenRotation + ") called");
|
|
enforcer.run(Stage.PREVIEW_STARTED, () -> {
|
|
Log.d(TAG, "setScreenRotation(" + screenRotation + ") executing");
|
|
this.screenRotation = screenRotation;
|
|
|
|
int previewRotation = getPreviewRotation(screenRotation);
|
|
int outputRotation = getOutputRotation(screenRotation);
|
|
|
|
Log.d(TAG, "Preview rotation: " + previewRotation + " Output rotation: " + outputRotation);
|
|
|
|
camera.setDisplayOrientation(previewRotation);
|
|
|
|
Camera.Parameters params = camera.getParameters();
|
|
params.setRotation(outputRotation);
|
|
camera.setParameters(params);
|
|
});
|
|
}
|
|
|
|
private void onCameraUnavailable() {
|
|
enforcer.reset();
|
|
eventListener.onCameraUnavailable();
|
|
}
|
|
|
|
private Properties getProperties() {
|
|
Camera.Size previewSize = camera.getParameters().getPreviewSize();
|
|
return new Properties(Camera.getNumberOfCameras(), previewSize.width, previewSize.height);
|
|
}
|
|
|
|
private Camera.Size getClosestSize(List<Camera.Size> sizes, int width, int height) {
|
|
Collections.sort(sizes, ASC_SIZE_COMPARATOR);
|
|
|
|
int i = 0;
|
|
while (i < sizes.size() && (sizes.get(i).width * sizes.get(i).height) < (width * height)) {
|
|
i++;
|
|
}
|
|
|
|
return sizes.get(Math.min(i, sizes.size() - 1));
|
|
}
|
|
|
|
private int getOutputRotation(int displayRotationCode) {
|
|
int degrees = convertRotationToDegrees(displayRotationCode);
|
|
|
|
Camera.CameraInfo info = new Camera.CameraInfo();
|
|
Camera.getCameraInfo(cameraId, info);
|
|
|
|
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
|
return (info.orientation + degrees) % 360;
|
|
} else {
|
|
return (info.orientation - degrees + 360) % 360;
|
|
}
|
|
}
|
|
|
|
private int getPreviewRotation(int displayRotationCode) {
|
|
int degrees = convertRotationToDegrees(displayRotationCode);
|
|
|
|
Camera.CameraInfo info = new Camera.CameraInfo();
|
|
Camera.getCameraInfo(cameraId, info);
|
|
|
|
int result;
|
|
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
|
result = (info.orientation + degrees) % 360;
|
|
result = (360 - result) % 360;
|
|
} else {
|
|
result = (info.orientation - degrees + 360) % 360;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private int convertRotationToDegrees(int screenRotation) {
|
|
switch (screenRotation) {
|
|
case Surface.ROTATION_0: return 0;
|
|
case Surface.ROTATION_90: return 90;
|
|
case Surface.ROTATION_180: return 180;
|
|
case Surface.ROTATION_270: return 270;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private final Comparator<Camera.Size> ASC_SIZE_COMPARATOR = (o1, o2) -> Integer.compare(o1.width * o1.height, o2.width * o2.height);
|
|
|
|
private enum Stage {
|
|
INITIALIZED, PREVIEW_STARTED
|
|
}
|
|
|
|
class Properties {
|
|
|
|
private final int cameraCount;
|
|
private final int previewWidth;
|
|
private final int previewHeight;
|
|
|
|
Properties(int cameraCount, int previewWidth, int previewHeight) {
|
|
this.cameraCount = cameraCount;
|
|
this.previewWidth = previewWidth;
|
|
this.previewHeight = previewHeight;
|
|
}
|
|
|
|
int getCameraCount() {
|
|
return cameraCount;
|
|
}
|
|
|
|
int getPreviewWidth() {
|
|
return previewWidth;
|
|
}
|
|
|
|
int getPreviewHeight() {
|
|
return previewHeight;
|
|
}
|
|
|
|
@Override
|
|
public @NonNull String toString() {
|
|
return "cameraCount: " + cameraCount + " previewWidth: " + previewWidth + " previewHeight: " + previewHeight;
|
|
}
|
|
}
|
|
|
|
interface EventListener {
|
|
void onPropertiesAvailable(@NonNull Properties properties);
|
|
void onCameraUnavailable();
|
|
}
|
|
|
|
interface CaptureCallback {
|
|
void onCaptureAvailable(@NonNull byte[] jpegData, boolean frontFacing);
|
|
}
|
|
}
|