How to Make an Android Camera App with Photo Filters? Full Code and Step by Step Guide
Tags: android, programming, tutorial, beginners
Published: 2023-01-06 12:12:57

Creating a camera app that has photo filters is a great way to learn about image processing in [Android](https://quokkalabs.com/android-app-development). In this tutorial, we will create a simple camera app that allows you to apply different filters to your photos.

We will start by setting up a basic layout that includes a preview of the camera and a button to take a picture. Then, we will add the ability to select different filters and apply them to the image. Finally, we will save the modified image to the device's storage.

[Clear here](https://quokkalabs.com/mobile-app-development) to get in touch with me if you got similar app ideas for your projects. 

Here is a step by step guide to creating your own camera app with photo filters:

 
- Create a new Android Studio project and select "Empty Activity" as the template.


- Add the following permissions to your AndroidManifest.xml file:

```
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

```

- Create a layout file for the main activity, activity_main.xml. Add a TextureView to display the camera preview and a Button to take a picture. Your layout should look something like this:

```
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextureView
        android:id="@+id/textureView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/button_capture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Capture" />

</RelativeLayout>

```

- In the MainActivity.java file, add the following instance variables:

```
private TextureView textureView;
private Button buttonCapture;
private String cameraId;
private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSessions;
private CaptureRequest.Builder captureRequestBuilder;
private Size imageDimension;
private ImageReader imageReader;

```

- Set up the camera in the onCreate() method of the MainActivity. First, initialize the textureView and buttonCapture variables:

```
textureView = (TextureView) findViewById(R.id.textureView);
buttonCapture = (Button) findViewById(R.id.button_capture);

```

Next, set up the TextureView to display the camera preview. Add the following code:

```
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
        openCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
        // Transform you image captured size according to the surface width and height
    }

    @Override
    public boolean onSurfaceTexture

```

- In the openCamera() method, we will set up the camera using the CameraManager class. Add the following code:

```
private void openCamera() {
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    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];
        // Add permission for camera and let user grant the permission
        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();
    }
}

```

- Now we will set up the cameraDevice and create a cameraCaptureSession to take pictures. Add the following code:

```
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        //This is called when the camera is open
        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 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) {
                //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(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
           

```

- In the updatePreview() method, we will start displaying the camera preview. Add the following code:

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

```

- Now we will add the ability to take a picture. In the onClick() method of the Button, add the following code:

```
buttonCapture.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        takePicture();
    }
});

private 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));
        final File file = new File(Environment.getExternalStorageDirectory() + "/pic.jpg");
        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

```

- Now we will add the ability to apply filters to the image. First, create a layout file for a FilterSelectionDialogFragment. This will be a simple layout with a RecyclerView to display the available filters and a Button to apply the selected filter.

```
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button_apply"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:text="Apply" />

</LinearLayout>

```

- Next, create a FilterSelectionAdapter to display the filters in the RecyclerView. This adapter will need to extend the RecyclerView.Adapter class and implement the ViewHolder pattern.

```
public class FilterSelectionAdapter extends RecyclerView.Adapter<FilterSelectionAdapter.ViewHolder> {

    private List<Filter> filters;
    private int selectedPosition = -1;

    public FilterSelectionAdapter(List<Filter> filters) {
        this.filters = filters;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_filter, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Filter filter = filters.get(position);
        holder.textView.setText(filter.getName());
        holder.imageView.setImageBitmap(filter.getThumbnail());
        holder.itemView.setSelected(selectedPosition == position);
    }

    @Override
    public int getItemCount() {
        return filters.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        ImageView imageView;
        TextView textView;

        public ViewHolder(View itemView) {
            super(itemView);
            imageView = (ImageView) itemView.findViewById(R.id.imageView);
            textView = (TextView) itemView.findViewById(R.id.textView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectedPosition = getAdapterPosition();
                    notifyDataSetChanged();
                }
            });
        }
    }
}

```

- Create a FilterSelectionDialogFragment to display the filters and allow the user to select one. This fragment will need to implement the DialogFragment class and include a RecyclerView and Button in its layout.

```
public class FilterSelectionDialogFrag

```

- In the applyFilter() method of the FilterSelectionDialogFragment, we will apply the selected filter to the image. First, we will create a method to apply the filter using the RenderScript framework:

```
private void applyFilter(Bitmap bitmap, Filter filter) {
    RenderScript rs = RenderScript.create(getActivity());
    Allocation input = Allocation.createFromBitmap(rs, bitmap);
    Allocation output = Allocation.createTyped(rs, input.getType());
    ScriptIntrinsicColorMatrix script = ScriptIntrinsicColorMatrix.create(rs);
    script.setColorMatrix(filter.getColorMatrix());
    script.forEach(output);
    output.copyTo(bitmap);
    input.destroy();
    output.destroy();
    script.destroy();
    rs.destroy();
}

```

- Next, we will retrieve the selected filter and apply it to the image:

```
Filter filter = filters.get(selectedPosition);
applyFilter(bitmap, filter);

```

- Finally, we will save the modified image to the device's storage:

```
private void saveBitmap(Bitmap bitmap) {
    FileOutputStream out = null;
    try {
        out = new FileOutputStream(imageFile);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (out != null) {
                out.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

```

- Finally, add a button to the main layout to open the FilterSelectionDialogFragment and display the available filters:

```
<Button
    android:id="@+id/button_filters"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_alignParentEnd="true"
    android:text="Filters" />

```

In the onClick() method of the Button, open the FilterSelectionDialogFragment:

```
buttonFilters.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        FilterSelectionDialogFragment fragment = new FilterSelectionDialogFragment();
        fragment.setImageFile(imageFile);
        fragment.show(getSupportFragmentManager(), "filters");
    }
});

```

And that's it! You now have a fully functional camera app with the ability to apply different filters to your photos. I hope you found this tutorial helpful. If you need any help, [reach me here](https://quokkalabs.com/android-app-development)!
Happy coding!

Blog banner by [wang_selina.](https://dribbble.com/shots/9775106-Filter-camera-APP/attachments/1805799?mode=media)