Samples

Sample Application Overview

The sample application opens multiple cameras and shows their preview images on a multi-view screen. The main purpose of the sample application is to demonstrate how to access each camera by their camera ID.

Development Environment

The sample application is developed under the following configuration:

• IDE: Android Studio 1.5

• Build: Gradle

• JDK: 8.0

 

Manifest configurations are as follows:

<SDK Version>

• minSdkVersion: 21

• targetSdkVersion: 23

 

<Permission>

• android.permission.CAMERA

• android.permission.WRITE_EXTERNAL_STORAGE

 

<Feature>

• android.hardware.camera

• android.hardware.camera.autofocus

Project Structure

The sample application consists of the following files:

 

File Location

Description

HelloDualCamera1Activity.java

Opens dual cameras and shows their preview.

activity_hello_dual_camera1.xml

Preview layout

AndroidManifest.xml

Manifest file

UI Layout

The UI layout of the sample application is shown in the following figure:

 

 

The upper section shows the preview of one of the dual cameras, and the lower shows the preview of other camera placed on the same side with the dual camera.

 

You can choose to divide the layout into multiple sections and assign preview from the cameras to the divided sections. In this way, the layout can display previews from multiple cameras.

 

Opening too many camera devices might reach the system-wide limit on the number of opened cameras imposed by the smartphones. In such case, ERROR_MAX_CAMERAS_IN_USE > error will be returned.

Source Code Analysis

This section provides a source code analysis on the sample application to demonstrate how cameras and their respective views are managed.

Positioning the view

The preview layout of the sample application is defined in the layout xml file (activitiy_hello_dual_camera1.xml). The sample application divides the layout into two sections. Each section consists of a TextureView and a Button, contained in FrameLayout. By so doing, the Button can be placed on top of the TextureView object. The TextureView object is used to hold preview image. The Button object is used to present camera ID and capture image. The following figure shows the layout composition in details.

 

 

The sample application gets the view of this layout from the HelloDualCamera1Activity class. The HelloDualCamera1Activity is the main activity declared in the manifest with the MAIN action and LAUNCHER category filter.

 

<AndroidManifest.xml>

<activity android:name=".HelloDualCamera1Activity"
    android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

 

<HelloDualCamera1Activity.java>

public class HelloDualCamera1Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_dual_camera1);
    }
}

Preparing a Surface for Preview

TextureView for View Holder

To show a camera preview to the user, prepare a holder class to draw surface that holds preview image stream. The sample application uses TextureView to display camera preview.

 

Listener to Create Preview

The TextureView.SurfaceTextureListener notifies the availability of TextureView’s SurfaceTexture. This SurfaceTexture is used as the holder of camera preview output from its associated camera.

 

When the SurfaceTexture becomes available, obtain a camera instance and start to present its preview image from the onSurfaceTextureAvailable() callback. See the subsequent Getting the Camera Instance and Presenting Preview from Camera sections for more details.

 

The onSurfaceTextureAvailable() callback is not called when the application enters sleep mode (the screen is turned off) and wakes up again (the screen is turned on). Since onResume()gets called when waking up, open the camera and start preview again from onResume() method.

 

Getting the Camera Instance

Camera Permission Request

In order to use camera features, the sample application must have camera access permissions. Declare the CAMERA permission and the android.hardware.camera feature in the manifest file. See Modifying the AndroidManifest.xml for more details.

 

Besides the manifest declaration, the sample application checks the permission at runtime. The checkCameraPermission() method examines whether the application has the CAMERA permission.

 

private boolean checkCameraPermission() {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        return false;
    }
    return true;
}

 

If the application does not have the CAMERA permission, it requests the CAMERA permission by using requestCameraPermission() method.

 

private void requestCameraPermission() {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
            PERMISSION_REQUEST_CAMERA);
}

 

Opening Camera Device

Now in order to access a particular hardware camera, get the instance of Camera object by using Camera.open() method. Camera.open() returns the primary camera by default. The Camera.open(int cameraId) method returns a particular Camera object, whose camera ID is specified by the cameraId parameter.

 

The sample application opens the two cameras facing the same direction, namely, dual cameras. The findDualCameraId() method determines the dual camera IDs by examining their lens facing direction. The Camera.getCameraInfo(int, Camera.CameraInfo) method provides the information of a camera including the facing of the camera.

 

private void findDualCameraId() {
    Camera.getCameraInfo(0, cameraInfo1);
    Camera.getCameraInfo(1, cameraInfo2);
    Camera.getCameraInfo(2, cameraInfo3);

    if (cameraInfo1.facing == cameraInfo2.facing) {
        cameraId1 = 0;
        cameraId2 = 1;
    } else if (cameraInfo1.facing == cameraInfo3.facing) {
        cameraId1 = 0;
        cameraId2 = 2;
    } else {
        cameraId1 = 1;
        cameraId2 = 2;
    }
}

 

In case of LG V10, the camera with ID 1 and the camera with ID 2 will be chosen (front facing dual cameras). In case of LG G5, the camera with ID 0 and the camera with ID 2 will be chosen (back facing dual cameras). Open those chosen cameras by using Camera.open(int) method.

 

On LG G5 device, open the camera with ID 2 first and then open the other camera with ID 0. This is necessary to use full camera resolution and avoid camera app crash.

 

Presenting Preview from Camera

Adjusting Preview Size and Orientation

You can get the list of supported preview sizes of a camera device by using Camera.Parameters.getSupportedPreviewSizes() method. Choose appropriate preview size from the list and apply it to the camera by using Camera.Parameters.setPreviewSize()method.

You can change the orientation of preview frame. Use Camera.setDisplayOrientation() method to rotate the preview frame in the specified amount of degrees clockwise.

 

Presenting Preview

Use the Camera.setPreviewTexture(SurfaceTexture) method to assign the SurfaceTexture obtained by TextureView.getSurfaceTexture() to present the preview from the associated camera. Then call Camera.startPreview() to start displaying the preview image.

 

private void startPreview(Camera camera, int cameraId, SurfaceTexture surface) {
    try {
        if(null != camera) {
            Log.i(TAG, "starting preview on camera ID: " + cameraId);
            camera.setPreviewTexture(surface);
            camera.startPreview();
        }
    } catch (IOException e) {
        Log.e(TAG, "Error setting camera preview: " + e.getMessage());
    }
}

 

Linking Preview from Camera with Surface View Holder

The TextureView object in the FrameLayout element of the layout xml file is used to display the preview. Link the SurfaceTexture of TextureView object with the camera by using setPreview() method.

 

textureView1 = (TextureView) findViewById(R.id.camera_preview1);
textureView2 = (TextureView) findViewById(R.id.camera_preview2);

startPreview(camera1, cameraId1, textureView1.getSurfaceTexture());
startPreview(camera2, cameraId2, textureView2.getSurfaceTexture());

Releasing the Camera

Once the application is done using the camera, or when the application leaves the activity, the camera instance should be properly released. The onPause() method is the right place to do so. Camera preview should be stopped first by using Camera.stopPreview() method. Then call Camera.release() to release the currently opened camera device.

 

@Override
protected void onPause() {
    if (null != camera2) {
        camera2.stopPreview();
        camera2.release();
        camera2 = null;
    }

    if (null != camera1) {
        camera1.stopPreview();
        camera1.release();
        camera1 = null;
    }

    super.onPause();
}

 

On LG G5 device, close the camera with ID 0 first and then open the other camera with ID 2 (in reverse order of opening). This is necessary to properly close camera devices and avoid camera app crash.

 

Taking a Picture (optional)

You can add capture functionality to the application.

 

Storage Permission Request

Since capturing images require access to the storage of the device, the application must have WRITE_EXTERNAL_STORAGE permission. Declare the WRITE_EXTERNAL_STORAGE permission in the manifest file. See Modifying the AndroidManifest.xml for more details.

 

Besides the manifest declaration, the sample application checks the permission at runtime. The checkStoragePermission() method examines whether the application has the WRITE_EXTERNAL_STORAGE permission.

 

private boolean checkCameraPermission() {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        return false;
    }
    return true;
}

 

If the application does not have the WRITE_EXTERNAL_STORAGE permission, it requests the WRITE_EXTERNAL_STORAGE permission by using requestStoragePermission() method.

 

private void requestCameraPermission() {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
            PERMISSION_REQUEST_STORAGE);
}

 

Adjusting Picture Size

You can get the list of supported preview sizes of a camera device by using Camera.Parameters.getSupportedPictureSizes(). Choose appropriate preview size from the list and apply it to the camera by using Camera.Parameters.setPicturewSize().

 

Be considerate of dual camera resolution constraints when choosing the picture size. See Constraints on Camera Resolution for more information. The sample application uses the resolution 3840x2160 (8MP) for G5 and 2560x1920 (5MP) for V10.

 

Taking a Picture from a Button

The Button object in the layout can be used as a shutter button. Add a OnClickListener() to the Button object in the layout and call Camera.takePicture() method from the listener. In addition, the sample application updates the text field of the Button to indicate the camera ID number.

 

button1 = (Button) findViewById(R.id.button1);
button1.setText(String.format("ID: %d", cameraId1));

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if(null != camera1) {
            camera1.takePicture(null, null, mPicture1);
        }
    }
});

 

The Camera.takePicture() method needs a callback to handle the captured image data from the camera. The sample application uses a callback for JPEG image data to save the image file.

 

private Camera.PictureCallback mPicture1 = new Camera.PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        ...

        camera.startPreview();
    }
};

 

After an image is taken, the preview image stops displaying on the TextureView. Call the Camera.startPreview() method to start the preview again.

Navigation