In this article, I am going to show you how you can be able to create an android background camera application in order record or capture an image from the camera without showing any preview using Service

The android camera API has some cool features and some restrictions in order to capture images or video form the hardware. most programmers try to create a camera service to achieve this goal without showing any preview.

The android camera API will let you use the camera hardware in the background but if you try to capture an image the image will get black and you will get nothing except the black image.

So I have seen a lot of questions about how to create a background camera and come across an idea to share this thing for those who need this help. You can get the full source code in my Github repository.

let's get into the main topic.

1. The first thing to do is to add the appropriate permission in our AndroidManifest.xml file as you can see in the below code. if we do not have the permission the device does not allow as to use the integrated camera so to avoid this thing we need to add the "android.permission.Camera" in the manifest. After we decleare our permission and feature the second thing we need to do is to create a service which we will use to run the camera in the background and capure the image without preview. so we will create a Service CameraService.java file and after that we need to create a Camera object

	
        package com.example.backgroundcamera;

        import android.app.Service;
        import android.os.IBinder;
        import android.content.Intent;

        public class CameraService extends Service {
            public static Camera camera = null;

            @Override
            public IBinder onBind(Intent intent) {
                return  null;
            }
        }
  
after we create the service we need to create a preview and holder using the built in SurfaceView and SurfaceHolder classes.


public class CameraService extends Service {
    public static Camera camera = null;
    
    @Override
    public IBinder onBind(Intent intent) {
        return  null;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        final SurfaceView preview = new SurfaceView(this);
        SurfaceHolder holder = preview.getHolder();
        preview.setZOrderOnTop(true);
        holder.setFormat(PixelFormat.TRANSLUCENT);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        return START_STICKY;
    }
}

in the above code we created a surface view and set to our holder and then we set the preview on to of other viewport and then we set the type to SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS - what this actually means is we will be updatating our preview constantly.

inorder to work with our camera we need to add a callback to our SurfaceHolder the we are going to initiate our Camera object ad set the Width and Height and other some camera parameters and we start to preview but.

this does not actually show a preview it's just to fool the device that we have a preview running.

        holder.addCallback(new Callback() {
            public void surfaceCreated(SurfaceHolder holder) {
                try {
                    camera = Camera.open();
                    int cHeight = 0;
                    int cWidth = 0;
                    Camera.Parameters params = camera.getParameters();
                    List sizes = params.getSupportedPictureSizes();
                    for(Camera.Size size : sizes) {
                        cHeight = size.height;
                        cWidth = size.width;
                    }
                    params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                    params.setRotation(90);
                    params.setPictureSize(cWidth, cHeight);
                    camera.setParameters(params);
                    try {
                        camera.setPreviewDisplay(holder);
                    } catch(IOException e) {}
                    camera.startPreview();
                } catch(Exception e) {}
            }
            public void surfaceDestroyed(SurfaceHolder holder) {}
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
        });

if we call the camera.takePicture(null, null, PictureCallback) the output image will be black so to avoid this from happening we need to set an os Handler to set when we want to snap the picture or how much time it has to wait before taking the picture.


                    final Handler handler = new Handler();
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            camera.takePicture(null, null, new Camera.PictureCallback() {
                                public void onPictureTaken(byte[] data, Camera camera) {
                                    Bitmap mybitmap = BitmapFactory.decodeByteArray(data,0,data.length);
                                    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                                    mybitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
                                    String dir = Environment.getExternalStorageDirectory()+"";
                                    try {
                                        File f = new File(dir, "myHiddenImage.jpeg");
                                        f.createNewFile();
                                        FileOutputStream fo = new FileOutputStream(f);
                                        fo.write(bytes.toByteArray());
                                        MediaScannerConnection.scanFile(CameraService.this, new String[] {f.getPath()}, new String[]{"image/jpeg"}, null);
                                        fo.close();
                                        showMessage("Picture Successfully Taken");
                                    } catch (Exception e) {}
                                    camera.release();
                                }
                            });
                        }
                    }, 2000);

the next thing we will add the preview to the window manager inorder to get rid of the black image and get the actual image rendered from the camera to do this we initiate a WindowManager object and set it's height and width to 1,1

Note if we decrease the width and height to lessthan 1 the app is not going to work. so to do this we will write this code in to the onStartCommand function.

        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1,1,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
                PixelFormat.TRANSPARENT
        );

        wm.addView(preview, params);

finally we will start our CameraService and finish the activity inorder to avoid being killed.


package com.razor.hiddencamerasnap;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        startService(new Intent(this, CameraService.class));
        finish(); //this function closes the main activity after starting the service
    }
}

Get The Full Source Code