0

I'm new to Android, and working on a project that would entail capturing an image from the camera and displaying it to the screen when a button is pressed, while at the same time allowing users to see the camera's content stream. While I have looked at several answers such as this link, they mainly dealt with displaying an image using the existing camera app. However, I would prefer to use the hardware itself. My code is largely derived from https://inducesmile.com/android/android-camera2-api-example-tutorial/. I am using Android Studio 3.2.1 and API 22 as my minimum sdk. Here is my code:

ACTIVITY_MAIN.XML LAYOUT FILE

  <?xml version="1.0" encoding="utf-8"?>
  <android.support.constraint.ConstraintLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:exported="true"
  tools:context=".MainActivity">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:gravity="end"
    android:orientation="vertical" >

    <TextureView
        android:id="@+id/texture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"  />

    <Button
        android:id="@+id/btn_takepicture"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/take_picture" />

    <ImageView
        android:id="@+id/animation_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" />

 </LinearLayout>
</android.support.constraint.ConstraintLayout>

STRINGS.XML FILE

<resources>
<string name="app_name">ColorDesignProjectAPI22</string>
<string name="take_picture">Take picture</string>
</resources>

MAINACTIVITY.JAVA FILE

  package com.user.colordesignprojectapi22;

  import android.support.v7.app.AppCompatActivity;
  import android.os.Bundle;
  import android.Manifest;
  import android.content.Context;
  import android.content.pm.PackageManager;
  import android.graphics.Bitmap;
  import android.graphics.ImageFormat;
  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.Environment;
  import android.os.Handler;
  import android.os.HandlerThread;
  import android.support.annotation.NonNull;
  import android.support.v4.app.ActivityCompat;
  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.ImageView;
  import android.widget.Toast;
  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.List;




public class MainActivity extends AppCompatActivity {
private static final String TAG = "AndroidCameraApi";
private Button takePictureButton;
private TextureView textureView;
private static final 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;
protected CameraDevice cameraDevice;
protected CameraCaptureSession cameraCaptureSessions;
protected CaptureRequest.Builder captureRequestBuilder;
private Size imageDimension;
private ImageReader imageReader;
private File file;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;
private Bitmap bitmap;
private ImageView imageView;
private String filepath;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textureView = findViewById(R.id.texture);
    assert textureView != null;
    textureView.setSurfaceTextureListener(textureListener);
    imageView = new ImageView(this);
    imageView = findViewById(R.id.animation_view);
    takePictureButton = findViewById(R.id.btn_takepicture);
    assert takePictureButton != null;
    takePictureButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            takePicture(); 
        }
    });
}

TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        openCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false; 
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }
};
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        Log.e(TAG, "onOpened");
        cameraDevice = camera;
        createCameraPreview();
    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        cameraDevice.close();
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        cameraDevice.close();
        cameraDevice = null;
    }
};

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();
    }
}

public 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);
        int rotation = getWindowManager().getDefaultDisplay().getRotation(); 
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
        filepath = Environment.getExternalStorageDirectory().toString();
        final File file = new File(filepath);
        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();
                    }
                }
            }
        };
        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(MainActivity.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();
    }
}

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.addTarget(surface);
        cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                if (null == cameraDevice) {
                    return;
                }
                cameraCaptureSessions = cameraCaptureSession;
                updatePreview(); 
            }
            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();                
            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

public 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);
                if(file.exists()) {
                    bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); 
                    if(bitmap!=null) {
                        System.out.println("LONG LIVE!");
                        imageView.setImageBitmap(bitmap);
                    }
                    if(bitmap==null){
                        System.out.println("HELP ME!");
                    }
                }
                if(!(file.exists()) {
                 System.out.println("No!!!!!");
                 }

    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

final CameraCaptureSession.CaptureCallback captureCallbackListener = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
        super.onCaptureCompleted(session, request, result);
        Toast.makeText(MainActivity.this, "Saved:" + file, Toast.LENGTH_SHORT).show();
        createCameraPreview();
    }
};
private void openCamera() {
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    Log.e(TAG, "is camera open");
    try {
        cameraId = manager.getCameraIdList()[0];
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        assert map != null;
        imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
            return;
        }
        manager.openCamera(cameraId, stateCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
    Log.e(TAG, "openCamera X");
}
private void closeCamera() {
    if (cameraDevice != null) {
        cameraDevice.close();
        cameraDevice = null;
    }
    if (imageReader != null) {
        imageReader.close();
        imageReader = null;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_CAMERA_PERMISSION) {
        if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
            Toast.makeText(MainActivity.this, "Sorry!!!, you can't use this app without granting permission", Toast.LENGTH_LONG).show();
            finish();
        }
    }
}

@Override
protected void onResume() {
    super.onResume();
    Log.e(TAG, "onResume");
    startBackgroundThread();
    if (textureView.isAvailable()) {
        openCamera();
    } else {
        textureView.setSurfaceTextureListener(textureListener);
    }
}

@Override
protected void onPause() {
    Log.e(TAG, "onPause");
    stopBackgroundThread();
    super.onPause();
 }
}

I'm sure the answer is as simple as adding a new view to the xml or something like that, but I've been looking through the documentation for an hour or so, and haven't found anything obvious.

Thanks in advance!

  • It seems like you are trying to [How to overlay a view in a constraintlayout?](https://stackoverflow.com/q/47140693/295004), your code seems to indicate that with the unattached `imageView` but your description is a bit unclear as to its placement. – Morrison Chang Jan 02 '19 at 04:35
  • @MorrisonChang Thank you for responding to me! I appreciate it. As for imageView, it is a variable representing the possible use of ImageView to display the single image rather than the content stream being represented by TextureView, as I had seen ImageView being used in other examples to display a single image. However, I'm unsure about its placement in the xml and how to use it at the same time as TextureView. – Henry McGuire Jan 02 '19 at 04:59
  • Not sure what is your question still. Either try it without overlapping then try overlapping or go for broke. If you asking about where is the best placement, that is up to you. There are lots of similar questions in StackOverflow covering overlaying. – Morrison Chang Jan 02 '19 at 05:18
  • @MorrisonChang Thank you for directing me to further resources about overlapping tools such as ConstraintLayout. I hadn't been thinking of my problem as a layout problem as much as a camera access problem, and appreciate your guiding me in the right direction. I will look more closely at layout resources such as the one you were kind enough to give me and try to clarify my question once I look through them. I hope to successfully implement your advice in the future! – Henry McGuire Jan 02 '19 at 05:43

0 Answers0