Как сохранить предварительный просмотр камеры при выключенном экране? - PullRequest
2 голосов
/ 18 января 2020

У меня небольшой проект. Мне нужно реализовать функцию обнаружения движения в android, чтобы закрыть экран устройства, если прошло более 5 минут, и включить его, если обнаружено движение.
Проблема в том, что предварительный просмотр камеры зависает, когда устройство собирается спит и не отправляет больше предварительных просмотров активности, поэтому приложение застряло с выключенным экраном. Я пробовал с замками, но это не работает. Он по-прежнему делает вызов приложения onPause, onStop.

Мой сервис камер:


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

Моя активность:


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

...