DEV Community

Cover image for How to Make an Android Camera App with Photo Filters? Full Code and Step by Step Guide
Dhruv Joshi
Dhruv Joshi

Posted on • Updated on

How to Make an Android Camera App with Photo Filters? Full Code and Step by Step Guide

Creating a camera app that has photo filters is a great way to learn about image processing in Android. 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 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" />

Enter fullscreen mode Exit fullscreen mode
  • 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>

Enter fullscreen mode Exit fullscreen mode
  • 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;

Enter fullscreen mode Exit fullscreen mode
  • 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);

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode
  • 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();
    }
}

Enter fullscreen mode Exit fullscreen mode
  • 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();


Enter fullscreen mode Exit fullscreen mode
  • 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();
    }
}

Enter fullscreen mode Exit fullscreen mode
  • 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

Enter fullscreen mode Exit fullscreen mode
  • 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>

Enter fullscreen mode Exit fullscreen mode
  • 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();
                }
            });
        }
    }
}

Enter fullscreen mode Exit fullscreen mode
  • 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

Enter fullscreen mode Exit fullscreen mode
  • 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();
}

Enter fullscreen mode Exit fullscreen mode
  • Next, we will retrieve the selected filter and apply it to the image:
Filter filter = filters.get(selectedPosition);
applyFilter(bitmap, filter);

Enter fullscreen mode Exit fullscreen mode
  • 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();
        }
    }
}

Enter fullscreen mode Exit fullscreen mode
  • 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" />

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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!
Happy coding!

Blog banner by wang_selina.

Top comments (2)

Collapse
 
rockmarks profile image
Martyn Harris

This tutorial looked good but abandoned it part way through as there are countless errors in the code, many missing braces, unneeded overrides, missing 'catches'. Do you have a working project to download?

Collapse
 
dhruvjoshi9 profile image
Dhruv Joshi

i deleted it too, actually i mistakenly copy pasted the gpt codes rther than posting corrected one....