How to keep the camera preview open when screen is off?

1.2k Views Asked by At

I have a small project. I need to implement a motion detection in android to close the device screen when more than 5 mins have passed, and wake it up if motion is detected.
The problem is that the camera preview freezes when the device is going to sleep, and doesn't send any more previews to the activity, so the application is stuck with the screen turned off.
I tried with wake locks but it doesn't work. It still makes the app call onPause, onStop.

My camera service:


import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.camera2.*;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;



public class CameraHandler  extends Service {
    private static final String TAG = "CameraHandler";

    private static final String START_SERVICE_COMMAND = "startServiceCommands";
    private static final int COMMAND_NONE = -1;
    private static final int COMMAND_START_RECORDING = 0;

    private Context context;
    private Camera camera;
    private boolean inPreview;

    private AtomicReference<byte[]> nextData = new AtomicReference<>();
    private AtomicInteger nextWidth = new AtomicInteger();
    private AtomicInteger nextHeight = new AtomicInteger();
    private IBinder cameraServiceBinder = new CameraServiceBinder();

    private Camera.PreviewCallback previewCallback;
    //private SurfaceHolder.Callback surfaceCallback;

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CameraHandler() {
    }

    public static void starService(Context context){

        Intent intent = new Intent(context, CameraHandler.class);
        intent.putExtra(START_SERVICE_COMMAND, COMMAND_START_RECORDING);
        context.startService(intent);
    }

    private void setPreviewHolder() {
       // previewHolder = this.surfaceView.getHolder();
        //previewHolder.addCallback(surfaceCallback);
        //previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

       // surfaceCallback.surfaceCreated(previewHolder);
        //surfaceCallback.surfaceChanged(previewHolder, 0, 0, 0);
        Log.i(TAG, "Surface was set");
    }

    public AtomicReference<byte[]> getNextData() {
        return nextData;
    }

    public AtomicInteger getNextWidth() {
        return nextWidth;
    }

    public AtomicInteger getNextHeight() {
        return nextHeight;
    }

    private boolean checkCameraHardware() {
        // if this device has a camera or not
        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
    }

    public void releaseCamera(){
        //if (previewHolder != null) {
       //     previewHolder.removeCallback(surfaceCallback);
      //  }
        if (camera != null){
            camera.setPreviewCallback(null);
            if (inPreview) camera.stopPreview();
            inPreview = false;
            camera.release();        // release the camera for other applications
            camera = null;
            Log.i(TAG, "Released camera");
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (intent == null) {
            throw new IllegalStateException("Must start the service with intent");
        }
        switch (intent.getIntExtra(START_SERVICE_COMMAND, COMMAND_NONE)) {
            case COMMAND_START_RECORDING:
                initialize();
                break;
            default:
                    throw new UnsupportedOperationException("Cannot start service with illegal commands");

        }
        return START_NOT_STICKY;
    }

    private void initialize(){

            camera = initializeCameraInstance();
            previewCallback = createCameraPreviewCallback();

            setPreviewHolder();

        if (camera != null) {
            SurfaceView sv = new SurfaceView(this);
            Log.i(TAG, "aci is still null!");
            WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
            WindowManager.LayoutParams params = new WindowManager.LayoutParams(1, 1,
                    WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                    PixelFormat.TRANSLUCENT);

            SurfaceHolder sh = sv.getHolder();
            sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

            sv.setZOrderOnTop(true);
            sh.setFormat(PixelFormat.TRANSPARENT);

            sh.addCallback(new SurfaceHolder.Callback() {
                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                    Camera.Parameters params = camera.getParameters();
                    camera.setParameters(params);
                    Camera.Parameters parameters = camera.getParameters();
                    Camera.Size size = getWorstPreviewSize(parameters);
                    if (size != null) {
                        parameters.setPreviewSize(size.width, size.height);
                        Log.d(TAG, "Using width=" + size.width + " height=" + size.height);
                    }
                    camera.setParameters(parameters);

                    try {
                        camera.setPreviewDisplay(holder);

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

                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

                    camera.setPreviewCallback(previewCallback);
                    camera.startPreview();
                    //mCamera.unlock();
                }

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {
                }
            });


            wm.addView(sv, params);

        }
    }

    private Camera initializeCameraInstance() {
        /*if (this.camera != null) {
            releaseCamera();
        }*/

        Camera camera = null;
        try {
            if (Camera.getNumberOfCameras() > 1) {
                //if you want to open front facing camera use this line
                camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
            } else {
                camera = Camera.open();
            }
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
            Log.i(TAG, "Kamera nicht zur Benutzung freigegeben");
        }
        return camera; // returns null if camera is unavailable
    }

    private void consumeData(byte[] data, int width, int height) {
        nextData.set(data);
        nextWidth.set(width);
        nextHeight.set(height);
    }

    private Camera.PreviewCallback createCameraPreviewCallback() {
        return new Camera.PreviewCallback() {

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

                Log.i(TAG, "-----Preview frame received-----");
                if (data == null) return;
                Camera.Size size = camera.getParameters().getPreviewSize();
                if (size == null) return;

                consumeData(data, size.width, size.height);
            }
        };
    }



    private static Camera.Size getWorstPreviewSize(Camera.Parameters parameters) {
        Camera.Size result = null;
        int width = Integer.MAX_VALUE;
        int height = Integer.MAX_VALUE;

        for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
            if (size.width <= width && size.height <= height) {
                if (result == null) {
                    result = size;
                } else {
                    int resultArea = result.width * result.height;
                    int newArea = size.width * size.height;

                    if (newArea < resultArea) result = size;
                }
            }
        }

        return result;
    }

    private static Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) {
        Camera.Size result = null;

        for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
            if (size.width <= width && size.height <= height) {
                if (result == null) {
                    result = size;
                } else {
                    int resultArea = result.width * result.height;
                    int newArea = size.width * size.height;

                    if (newArea > resultArea) result = size;
                }
            }
        }

        return result;
    }

    public class CameraServiceBinder extends Binder {
        public CameraHandler getService() {
            return CameraHandler.this;
        }
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return cameraServiceBinder;
    }
}

My Activity:


import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.SurfaceView;
import android.widget.TextView;
import android.util.Log;


public class MotionDetectorActivity extends Activity {
    private static final String TAG = "MotionDetectorActivity";

    //private SurfaceView surfaceView;
    private TextView txtStatus;
    private MotionDetectorService motionDetectorService;
    private boolean serviceBounded = false;
    private ServiceConnection serviceConnection = createServiceConnection();

   // private CameraHandler cameraHandler;
    private AndroidScreenManager screenManager;
    private long lastMotion;

    PowerManager.WakeLock partialWakeLock;

    private void createWakeLocks() {
        PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
        //fullWakeLock = powerManager.newWakeLock(/*PowerManager.PARTIAL_WAKE_LOCK | */PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.FULL_WAKE_LOCK,
        //        "MotionDetectorActivity::FullWakelockTag");
        partialWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
                "MotionDetectorActivity::PartialWakelockTag");
    }

    private ServiceConnection createServiceConnection() {
        return new ServiceConnection() {
            public void onServiceConnected(ComponentName cName, IBinder serviceBinder) {
                Log.i(TAG, "Service is getting connected");
                MotionDetectorServiceBinder binder = (MotionDetectorServiceBinder) serviceBinder;
                MotionDetectorActivity.this.motionDetectorService = binder.getService();
                serviceBounded = true;

                //motionDetectorService.setCameraHandler(cameraHandler);
                motionDetectorService.setMotionDetectorCallback(new IMotionDetectorCallback() {
                    @Override
                    public void onMotionDetected() {
                        Log.v(TAG, "Motion detected");
                        txtStatus.setText("Motion detected");
                        lastMotion = System.currentTimeMillis();

                        if (!screenManager.isScreenOn()) {
                            Log.v(TAG, "Screen is turned off so waking it up");
                            screenManager.turnOn();
                            if (partialWakeLock.isHeld()) {
                                partialWakeLock.release();
                            }
                        }
                        /*if (!fullWakeLock.isHeld()) {
                            fullWakeLock.acquire();
                        }*/
                        final Handler handler = new Handler();
                        handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                txtStatus.setText("No motion detected");
                                /*if (fullWakeLock.isHeld()) {
                                    fullWakeLock.release();
                                }*/
                            }
                        }, 750);
                    }

                    @Override
                    public void onNoMotionDetected() {
                        long now = System.currentTimeMillis();
                        if (now - lastMotion > 5*1000) {
                            Log.i(TAG, "No motion for more than 5 seconds, turning screen off");
                            //screenManager.turnOff();
                        }
                    }

                    @Override
                    public void onTooDark() {
                        Log.v(TAG, "Too dark here");
                        txtStatus.setText("Too dark here");
                    }
                });

                // Custom config options
                motionDetectorService.setCheckIntervalMs(500);
                motionDetectorService.setLeniency(30);
                motionDetectorService.setMinLuma(500);
            }

            public void onServiceDisconnected(ComponentName cName){
                serviceBounded = false;
            }
        };
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       // surfaceView = (SurfaceView) findViewById(R.id.surfaceView);


        screenManager = new AndroidScreenManager(this);
        CameraHandler.starService(this);
        createWakeLocks();
        txtStatus = (TextView) findViewById(R.id.txtStatus);
        lastMotion = System.currentTimeMillis();
    }

    @Override
    protected void onStart() {
        super.onStart();

        Intent motionDetectorServiceIntent = new Intent(this, MotionDetectorService.class);
        bindService(motionDetectorServiceIntent, serviceConnection, BIND_AUTO_CREATE);
        startService(motionDetectorServiceIntent);

    }
    @Override
    protected void onResume() {
        Log.i(TAG, "-----onResume-----");
        super.onResume();

        /*if (motionDetectorService != null) {
            motionDetectorService.releasePartialWakeLocK();
        }

        if (partialWakeLock.isHeld()) {
            partialWakeLock.release();
            Log.v(TAG, "Released partial wake lock in the activity");
        }*/
    }

    private void acquirePartialWakeLock() {
        if (!partialWakeLock.isHeld()) {
            partialWakeLock.acquire();
            Log.v(TAG, "Acquired partial wake lock in the activity");
        }
    }

    @Override
    protected void onPause() {
        Log.i(TAG, "-----onPause-----");
        //CameraHandler cameraHandler = new CameraHandler(this, surfaceView);
        /*motionDetectorService.acquirePartialWakeLocK();*/
        //acquirePartialWakeLock();
        super.onPause();
    }

    private void unbindServiceIfBounded() {
        if (serviceBounded) {
            unbindService(serviceConnection);
            serviceBounded = false;
            Log.i(TAG, "Service is unbounded");
        }
    }

    @Override
    protected void onDestroy() {
        Log.v(TAG, "Destroying app");
        motionDetectorService.onDestroy();
        unbindServiceIfBounded();
        super.onDestroy();
    }

    @Override
    protected void onStop() {
        Log.i(TAG, "-----onStop-----");
        //cameraHandler.releaseCamera();
        //CameraHandler cameraHandler = new CameraHandler(this, surfaceView);
        //acquirePartialWakeLock();
        //unbindServiceIfBounded();
        super.onStop();
    }
}

0

There are 0 best solutions below