3

Problem statement:

When someone tries to open the device with the wrong pattern / PIN, my application should trigger an alarm, send an alert SMS to the registered mobile number. AND it should capture the image of the person trying to unlock the device, and send this image to registered Email ID.

What I have achieved:

  • I am getting notification of wrong Pattern / PIN in my DeviceAdmin class.
  • I start the service for background tasks. This service plays alarm successfully.
  • I send an alert SMS to the registered mobile number. I sent an alert Email to the registered email ID successfully. (BUT without image.)

I am confused about how can I capture image in background IntentService when the device is locked, and that, too without preview.

I cannot use the Camera intent obviously. Because startActivityForResult can't be called from Service. PLUS, I don't want user to capture the image after opening the Camera app.

My research led me to these posts already.

Can I use Android Camera in service without preview?

How to Capture Image When Device is Locked

Issue is:

Camera API is deprecate`. Camera2 API requires Minimum sdk version 21,

but my client's requirement is minSdkVersion 15, which I can't change. I am unable to figure out what should I do now. Any reference or help please?

Bruno
  • 3,872
  • 4
  • 20
  • 37
Ana
  • 166
  • 1
  • 16
  • 2
    Use the `Camera` API. "Deprecated" in Android means "we have something else that we think that you should consider" -- `android.hardware.Camera` continues to work (as well as it ever did). Note that you will need to use a foreground service on Android 9.0+, for privacy reasons. – CommonsWare Aug 16 '18 at 11:40

1 Answers1

3

I solved my issue with help of this blog

So I capture the image within background service using this code:

@Override
public void onStart(Intent intent, int startId) {
    mCamera = getAvailableFrontCamera();     // globally declared instance of camera
    if (mCamera == null){
        mCamera = Camera.open();    //Take rear facing camera only if no front camera available
    }
    SurfaceView sv = new SurfaceView(getApplicationContext());
    SurfaceTexture surfaceTexture = new SurfaceTexture(10);

    try {
        mCamera.setPreviewTexture(surfaceTexture);
        //mCamera.setPreviewDisplay(sv.getHolder());
        parameters = mCamera.getParameters();

        //set camera parameters
        mCamera.setParameters(parameters);


        //This boolean is used as app crashes while writing images to file if simultaneous calls are made to takePicture
        if(safeToCapture) {
            mCamera.startPreview();
            mCamera.takePicture(null, null, mCall);
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


    //Get a surface
    sHolder = sv.getHolder();
    //tells Android that this surface will have its data constantly replaced
    sHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

Camera.PictureCallback mCall = new Camera.PictureCallback()
{

    public void onPictureTaken(byte[] data, Camera camera)
    {
        safeToCapture = false;
        //decode the data obtained by the camera into a Bitmap

        FileOutputStream outStream = null;
        try{

            // create a File object for the parent directory
            File myDirectory = new File(Environment.getExternalStorageDirectory()+"/Test");
            // have the object build the directory structure, if needed.
            myDirectory.mkdirs();

            //SDF for getting current time for unique image name
            SimpleDateFormat curTimeFormat = new SimpleDateFormat("ddMMyyyyhhmmss");
            String curTime = curTimeFormat.format(new java.util.Date());

            // create a File object for the output file
            outStream = new FileOutputStream(myDirectory+"/user"+curTime+".jpg");
            outStream.write(data);
            outStream.close();
            mCamera.release();
            mCamera = null;

            String strImagePath = Environment.getExternalStorageDirectory()+"/"+myDirectory.getName()+"/user"+curTime+".jpg";
            sendEmailWithImage(strImagePath);
            Log.d("CAMERA", "picture clicked - "+strImagePath);
        } catch (FileNotFoundException e){
            Log.d("CAMERA", e.getMessage());
        } catch (IOException e){
            Log.d("CAMERA", e.getMessage());
        }

        safeToCapture = true;    //Set a boolean to true again after saving file.

    }
};

private Camera getAvailableFrontCamera (){

    int cameraCount = 0;
    Camera cam = null;
    Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
    cameraCount = Camera.getNumberOfCameras();
    for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
        Camera.getCameraInfo(camIdx, cameraInfo);
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            try {
                cam = Camera.open(camIdx);
            } catch (RuntimeException e) {
                Log.e("CAMERA", "Camera failed to open: " + e.getLocalizedMessage());
            }
        }
    }

    return cam;
}


//Send Email using javamail API as user will not be allowed to choose available
// application using a Chooser dialog for intent.
public void sendEmailWithImage(String imageFile){
    .
    .
    .
}

Following uses-features and permissions will be needed for this in manifest file:

<uses-feature
    android:name="android.hardware.camera"
    android:required="false" />
<uses-feature
    android:name="android.hardware.camera.front"
    android:required="false" />

<uses-permission android:name="android.permission.SEND_MAIL" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

I have set property required to false, so that user will be able to install my app even without any of the Camera available. May be this can help someone in future.

Ana
  • 166
  • 1
  • 16
  • Remove the boilerplate code. Like the SurfaceView, which is not being used. If you are using SurfaceTexture then there is no need to use the SurfaceView. Also the storage method is not working on android 11+ ( Tested). Due to the implementation of the scoped storage. – Jashanpreet singh Chakkal Sep 19 '21 at 14:01