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