|
|
@@ -0,0 +1,445 @@
|
|
|
+package com.miekir.ocr;
|
|
|
+
|
|
|
+import android.Manifest;
|
|
|
+import android.annotation.SuppressLint;
|
|
|
+import android.content.Context;
|
|
|
+import android.content.res.Configuration;
|
|
|
+import android.graphics.ImageFormat;
|
|
|
+import android.graphics.Point;
|
|
|
+import android.graphics.SurfaceTexture;
|
|
|
+import android.hardware.camera2.CameraAccessException;
|
|
|
+import android.hardware.camera2.CameraCaptureSession;
|
|
|
+import android.hardware.camera2.CameraCharacteristics;
|
|
|
+import android.hardware.camera2.CameraDevice;
|
|
|
+import android.hardware.camera2.CameraManager;
|
|
|
+import android.hardware.camera2.CameraMetadata;
|
|
|
+import android.hardware.camera2.CaptureRequest;
|
|
|
+import android.hardware.camera2.TotalCaptureResult;
|
|
|
+import android.hardware.camera2.params.StreamConfigurationMap;
|
|
|
+import android.media.Image;
|
|
|
+import android.media.ImageReader;
|
|
|
+import android.os.Bundle;
|
|
|
+import android.os.Environment;
|
|
|
+import android.os.Handler;
|
|
|
+import android.os.HandlerThread;
|
|
|
+import android.util.Log;
|
|
|
+import android.util.Size;
|
|
|
+import android.util.SparseIntArray;
|
|
|
+import android.view.Surface;
|
|
|
+import android.view.TextureView;
|
|
|
+import android.view.View;
|
|
|
+import android.widget.Button;
|
|
|
+import android.widget.Toast;
|
|
|
+
|
|
|
+import androidx.annotation.NonNull;
|
|
|
+
|
|
|
+import com.miekir.ocr.base.BaseCameraActivity;
|
|
|
+import com.miekir.ocr.tool.Utils;
|
|
|
+import com.miekir.ocr.view.AutoFitTextureView;
|
|
|
+import com.tbruyelle.rxpermissions2.RxPermissions;
|
|
|
+
|
|
|
+import java.io.File;
|
|
|
+import java.io.FileNotFoundException;
|
|
|
+import java.io.FileOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.OutputStream;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.concurrent.Semaphore;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+import static android.os.Environment.DIRECTORY_PICTURES;
|
|
|
+
|
|
|
+public class CameraActivity extends BaseCameraActivity {
|
|
|
+
|
|
|
+ private static SparseIntArray ORIENTATIONS = new SparseIntArray();
|
|
|
+
|
|
|
+ static {
|
|
|
+ ORIENTATIONS.append(Surface.ROTATION_0, 90);
|
|
|
+ ORIENTATIONS.append(Surface.ROTATION_90, 0);
|
|
|
+ ORIENTATIONS.append(Surface.ROTATION_180, 270);
|
|
|
+ ORIENTATIONS.append(Surface.ROTATION_270, 180);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String cameraId;
|
|
|
+ private CameraDevice cameraDevice;
|
|
|
+ private CameraCaptureSession cameraCaptureSessions;
|
|
|
+ private CaptureRequest.Builder captureRequestBuilder;
|
|
|
+ private Size imageDimension;
|
|
|
+ private ImageReader imageReader;
|
|
|
+
|
|
|
+ private File file = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES) + "/" + System.currentTimeMillis() + ".jpg");
|
|
|
+
|
|
|
+ private static final int REQUEST_CAMERA_PERMISSION = 200;
|
|
|
+
|
|
|
+ private Handler mBackgroundHandler;
|
|
|
+ private HandlerThread mBackgroundThread;
|
|
|
+
|
|
|
+ private AutoFitTextureView textureView;
|
|
|
+ private Button button;
|
|
|
+
|
|
|
+ private Semaphore mCameraOpenCloseLock = new Semaphore(1);
|
|
|
+
|
|
|
+ private static final int MAX_PREVIEW_WIDTH = 1920;
|
|
|
+ private static final int MAX_PREVIEW_HEIGHT = 1080;
|
|
|
+ private int mSensorOrientation;
|
|
|
+
|
|
|
+ private TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
|
|
|
+ @Override
|
|
|
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
|
|
+ openCamera(width, height);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
|
|
|
+ if (null != textureView || null == imageDimension) {
|
|
|
+ textureView.setTransform(Utils.configureTransform(width, height, imageDimension, CameraActivity.this));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
|
|
|
+ @Override
|
|
|
+ public void onOpened(CameraDevice camera) {
|
|
|
+
|
|
|
+ Log.e("tag", "onOpened");
|
|
|
+ mCameraOpenCloseLock.release();
|
|
|
+ cameraDevice = camera;
|
|
|
+ createCameraPreview();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onDisconnected(CameraDevice camera) {
|
|
|
+ mCameraOpenCloseLock.release();
|
|
|
+ cameraDevice.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(CameraDevice camera, int error) {
|
|
|
+ mCameraOpenCloseLock.release();
|
|
|
+ cameraDevice.close();
|
|
|
+ cameraDevice = null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int getLayoutID() {
|
|
|
+ return R.layout.activity_camera;
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("CheckResult")
|
|
|
+ @Override
|
|
|
+ public void initViews(Bundle savedInstanceState) {
|
|
|
+ textureView = (AutoFitTextureView) findViewById(R.id.textureView);
|
|
|
+ button = (Button) findViewById(R.id.button);
|
|
|
+
|
|
|
+ textureView.setSurfaceTextureListener(textureListener);
|
|
|
+
|
|
|
+ button.setOnClickListener(new View.OnClickListener() {
|
|
|
+ @Override
|
|
|
+ public void onClick(View v) {
|
|
|
+ takePicture();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private void openCamera(int width, int height) {
|
|
|
+ CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
|
|
|
+ Log.e("tag", "is camera open");
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
|
|
|
+ throw new RuntimeException("Time out waiting to lock camera opening.");
|
|
|
+ }
|
|
|
+ cameraId = manager.getCameraIdList()[0];
|
|
|
+ CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
|
|
|
+ StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
|
|
+ assert map != null;
|
|
|
+
|
|
|
+ Size largest = Collections.max(
|
|
|
+ Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
|
|
|
+ new Utils.CompareSizesByArea());
|
|
|
+
|
|
|
+
|
|
|
+ int displayRotation = getWindowManager().getDefaultDisplay().getRotation();
|
|
|
+ //noinspection ConstantConditions
|
|
|
+ mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
|
|
|
+ boolean swappedDimensions = false;
|
|
|
+ switch (displayRotation) {
|
|
|
+ case Surface.ROTATION_0:
|
|
|
+ case Surface.ROTATION_180:
|
|
|
+ if (mSensorOrientation == 90 || mSensorOrientation == 270) {
|
|
|
+ swappedDimensions = true;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case Surface.ROTATION_90:
|
|
|
+ case Surface.ROTATION_270:
|
|
|
+ if (mSensorOrientation == 0 || mSensorOrientation == 180) {
|
|
|
+ swappedDimensions = true;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ Log.e("tag", "Display rotation is invalid: " + displayRotation);
|
|
|
+ }
|
|
|
+
|
|
|
+ Point displaySize = new Point();
|
|
|
+ getWindowManager().getDefaultDisplay().getSize(displaySize);
|
|
|
+ int rotatedPreviewWidth = width;
|
|
|
+ int rotatedPreviewHeight = height;
|
|
|
+ int maxPreviewWidth = displaySize.x;
|
|
|
+ int maxPreviewHeight = displaySize.y;
|
|
|
+
|
|
|
+ if (swappedDimensions) {
|
|
|
+ rotatedPreviewWidth = height;
|
|
|
+ rotatedPreviewHeight = width;
|
|
|
+ maxPreviewWidth = displaySize.y;
|
|
|
+ maxPreviewHeight = displaySize.x;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
|
|
|
+ maxPreviewWidth = MAX_PREVIEW_WIDTH;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
|
|
|
+ maxPreviewHeight = MAX_PREVIEW_HEIGHT;
|
|
|
+ }
|
|
|
+
|
|
|
+ imageDimension = Utils.chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
|
|
|
+ rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
|
|
|
+ maxPreviewHeight, largest);
|
|
|
+
|
|
|
+ int orientation = getResources().getConfiguration().orientation;
|
|
|
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
|
+ textureView.setAspectRatio(
|
|
|
+ imageDimension.getWidth(), imageDimension.getHeight());
|
|
|
+ } else {
|
|
|
+ textureView.setAspectRatio(
|
|
|
+ imageDimension.getHeight(), imageDimension.getWidth());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (null != textureView || null == imageDimension) {
|
|
|
+ textureView.setTransform(Utils.configureTransform(width, height, imageDimension, CameraActivity.this));
|
|
|
+ }
|
|
|
+
|
|
|
+ manager.openCamera(cameraId, stateCallback, null);
|
|
|
+ } catch (CameraAccessException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ Log.e("tag", "openCamera X");
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void createCameraPreview() {
|
|
|
+ try {
|
|
|
+ SurfaceTexture texture = textureView.getSurfaceTexture();
|
|
|
+ assert texture != null;
|
|
|
+ texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
|
|
|
+ Surface surface = new Surface(texture);
|
|
|
+ captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
|
|
|
+ captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
|
|
+ captureRequestBuilder.addTarget(surface);
|
|
|
+ cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
|
|
|
+ @Override
|
|
|
+ public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
|
|
|
+ //The camera is already closed
|
|
|
+ if (null == cameraDevice) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // When the session is ready, we start displaying the preview.
|
|
|
+ cameraCaptureSessions = cameraCaptureSession;
|
|
|
+ updatePreview();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
|
|
|
+ Toast.makeText(CameraActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
|
|
|
+ }
|
|
|
+ }, null);
|
|
|
+ } catch (CameraAccessException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void updatePreview() {
|
|
|
+ if (null == cameraDevice) {
|
|
|
+ Log.e("tag", "updatePreview error, return");
|
|
|
+ }
|
|
|
+ captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
|
|
|
+ try {
|
|
|
+ cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
|
|
|
+ } catch (CameraAccessException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void startBackgroundThread() {
|
|
|
+ mBackgroundThread = new HandlerThread("Camera Background");
|
|
|
+ mBackgroundThread.start();
|
|
|
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void stopBackgroundThread() {
|
|
|
+ mBackgroundThread.quitSafely();
|
|
|
+ try {
|
|
|
+ mBackgroundThread.join();
|
|
|
+ mBackgroundThread = null;
|
|
|
+ mBackgroundHandler = null;
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void closeCamera() {
|
|
|
+ try {
|
|
|
+ mCameraOpenCloseLock.acquire();
|
|
|
+ if (null != cameraCaptureSessions) {
|
|
|
+ cameraCaptureSessions.close();
|
|
|
+ cameraCaptureSessions = null;
|
|
|
+ }
|
|
|
+ if (null != cameraDevice) {
|
|
|
+ cameraDevice.close();
|
|
|
+ cameraDevice = null;
|
|
|
+ }
|
|
|
+ if (null != imageReader) {
|
|
|
+ imageReader.close();
|
|
|
+ imageReader = null;
|
|
|
+ }
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ throw new RuntimeException("Interrupted while trying to lock camera closing.");
|
|
|
+ } finally {
|
|
|
+ mCameraOpenCloseLock.release();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void onResume() {
|
|
|
+ super.onResume();
|
|
|
+ Log.e("tag", "onResume");
|
|
|
+ startBackgroundThread();
|
|
|
+ if (textureView.isAvailable()) {
|
|
|
+ openCamera(textureView.getWidth(), textureView.getHeight());
|
|
|
+ } else {
|
|
|
+ textureView.setSurfaceTextureListener(textureListener);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void onPause() {
|
|
|
+ Log.e("tag", "onPause");
|
|
|
+ closeCamera();
|
|
|
+ stopBackgroundThread();
|
|
|
+ super.onPause();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void takePicture() {
|
|
|
+ if (null == cameraDevice) {
|
|
|
+ Log.e("tag", "cameraDevice is null");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
|
|
|
+ try {
|
|
|
+ CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
|
|
|
+ Size[] jpegSizes = null;
|
|
|
+ if (characteristics != null) {
|
|
|
+ jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
|
|
|
+ }
|
|
|
+ int width = 640;
|
|
|
+ int height = 480;
|
|
|
+ if (jpegSizes != null && 0 < jpegSizes.length) {
|
|
|
+ width = jpegSizes[0].getWidth();
|
|
|
+ height = jpegSizes[0].getHeight();
|
|
|
+ }
|
|
|
+ ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
|
|
|
+ List<Surface> outputSurfaces = new ArrayList<Surface>(2);
|
|
|
+ outputSurfaces.add(reader.getSurface());
|
|
|
+ outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
|
|
|
+ final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
|
|
|
+ captureBuilder.addTarget(reader.getSurface());
|
|
|
+ captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
|
|
|
+ // Orientation
|
|
|
+ int rotation = getWindowManager().getDefaultDisplay().getRotation();
|
|
|
+ captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
|
|
|
+
|
|
|
+
|
|
|
+ reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
|
|
|
+
|
|
|
+ final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
|
|
|
+ @Override
|
|
|
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
|
|
|
+ super.onCaptureCompleted(session, request, result);
|
|
|
+
|
|
|
+ Toast.makeText(CameraActivity.this, "Saved:" + file, Toast.LENGTH_SHORT).show();
|
|
|
+ createCameraPreview();
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
|
|
|
+ @Override
|
|
|
+ public void onConfigured(CameraCaptureSession session) {
|
|
|
+ try {
|
|
|
+ session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
|
|
|
+ } catch (CameraAccessException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onConfigureFailed(CameraCaptureSession session) {
|
|
|
+ }
|
|
|
+ }, mBackgroundHandler);
|
|
|
+ } catch (CameraAccessException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
|
|
|
+ @Override
|
|
|
+ public void onImageAvailable(ImageReader reader) {
|
|
|
+
|
|
|
+ Image image = null;
|
|
|
+ try {
|
|
|
+ image = reader.acquireLatestImage();
|
|
|
+ ByteBuffer buffer = image.getPlanes()[0].getBuffer();
|
|
|
+ byte[] bytes = new byte[buffer.capacity()];
|
|
|
+ buffer.get(bytes);
|
|
|
+ save(bytes);
|
|
|
+ } catch (FileNotFoundException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ } finally {
|
|
|
+ if (image != null) {
|
|
|
+ image.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void save(byte[] bytes) throws IOException {
|
|
|
+ OutputStream output = null;
|
|
|
+ try {
|
|
|
+ output = new FileOutputStream(file);
|
|
|
+ output.write(bytes);
|
|
|
+ } finally {
|
|
|
+ if (null != output) {
|
|
|
+ output.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+}
|