3

I'm trying to take a picture without showing the user anything (no view) through a service. This question has been asked several times before and I've gone through all I could find. Some similar questions:

Most of the questions link to other questions without providing a new solution.

I believe this is the best way to solve this problem: https://stackoverflow.com/a/10268650/3860594 Unfortunately the person has not provided a complete answer and I'm having trouble reproducing his method.

What I'm trying to do is create a SurfaceView inside a SurfaceHolder. I will then use WindowManager with the SurfaceView to create a floating window of sorts that is completely transparent so that it's hidden from the user. Please correct me if I'm wrong.

Here is my code:

SurfaceView mview = new SurfaceView(this);
SurfaceHolder mholder = new SurfaceHolder() {
    @Override
    public void addCallback(Callback callback) {

    }

    @Override
    public void removeCallback(Callback callback) {

    }

    @Override
    public boolean isCreating() {
        return false;
    }

    @Override
    public void setType(int type) {

    }

    @Override
        public void setFixedSize(int width, int height) {
    }

    @Override
    public void setSizeFromLayout() {
    }

    @Override
    public void setFormat(int format) {

    }

    @Override
    public void setKeepScreenOn(boolean screenOn) {
    }

    @Override
    public Canvas lockCanvas() {
        return null;
    }

    @Override
    public Canvas lockCanvas(Rect dirty) {
        return null;
    }

    @Override
        public void unlockCanvasAndPost(Canvas canvas) {

    }

    @Override
    public Rect getSurfaceFrame() {
        return null;
    }

    @Override
    public Surface getSurface() {
        return null;
    }
};


WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
        WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
        PixelFormat.TRANSLUCENT);
wm.addView(mview, params);
mview.setZOrderOnTop(true);
mholder.setFormat(PixelFormat.TRANSPARENT);


try {
    camera.setPreviewDisplay(mview.getHolder());
    camera.startPreview();
    camera.takePicture(null,null,photoCallback);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

None of this works as the usual message RuntimeException: takePicture failed is shown. Any help would be awesome, thanks.

Community
  • 1
  • 1
john
  • 1,561
  • 3
  • 20
  • 44
  • Try to set the window not fully transparent and check if it really shows the preview on screen. – Alex Cohn Sep 25 '15 at 13:36
  • @AlexCohn thanks for your suggestion man, I tried setting it to opaque but I'm getting the same error. – john Sep 25 '15 at 13:52
  • The question was, do you see camera stream there? – Alex Cohn Sep 25 '15 at 16:08
  • @AlexCohn No, app launches and then crashes immediately with no floating windows or view finders. – john Sep 25 '15 at 16:32
  • Wait a sec, you saw `takePicture failed` before. Isn't this crash different? – Alex Cohn Sep 25 '15 at 18:17
  • Nope, this crash looked exactly the same, both on the phone, as well as the error on AStudio. – john Sep 25 '15 at 19:14
  • BTW, it is not clear what its the role of **mholder** field in your code snippet. – Alex Cohn Sep 25 '15 at 19:27
  • My bad: I didn't notice this mistake before. You should never call takePicture() immediately after addView(). You must wait at least for the `surfaceCreated()` callback. – Alex Cohn Sep 25 '15 at 19:34
  • `mholder` is only used in this code snippet in my application. It seems, mholder is actually not doing anything useful at all :/ Also, how can I wait for the surface created callback? (google search didn't help) thanks – john Sep 26 '15 at 05:54
  • mview.getHolder().addCallback() – Alex Cohn Sep 26 '15 at 06:17
  • I tried `SystemClock.sleep(20000);` right before `takePicture` and this time the phone screen turned black for a second right before the application crashed. Same error though. – john Sep 26 '15 at 06:47
  • Thanks, I added a callback and now there is no error and the app does not crash. However, the picture that was saved to storage was completely black. – john Sep 26 '15 at 06:56
  • The black screen is NOT an empty image (200kb size).. I think it's because the camera view is not loading properly, the captured image is black. Any ideas? Thanks – john Sep 26 '15 at 07:02
  • is the preview also black? – Alex Cohn Sep 26 '15 at 13:59
  • There is no preview that appears on the screen (I tried both opaque and transparent), Logcat however, shows this warning: `I/Choreographer﹕ Skipped 395 frames! The application may be doing too much work on its main thread. W/InputEventReceiver﹕ Attempted to finish an input event but the input event receiver has already been disposed.` – john Sep 27 '15 at 05:04
  • How do you open the camera? – Alex Cohn Sep 27 '15 at 11:02
  • I don't open the camera interface (if thats what you're asking), I want to directly take the picture without showing the user anything. As for how I open the `camera` object, you can see it in this screenshot: http://i.imgur.com/F5sXvgI.png – john Sep 27 '15 at 11:45
  • The choreographer message is expected, but maybe you can reduce the tasks that use the UI thread. But the second is strange: do you dispose the SurfaceView too early? – Alex Cohn Sep 27 '15 at 14:13
  • My project has a main activity, from which I start a service (onCreate), and I'm trying to run this method on the service. I'm not doing anything else on the UI. I did not dispose of SurfaceView anywhere. Maybe it's not created properly? Is that possible? – john Sep 27 '15 at 15:18
  • Maybe your mistake is that you create the SurfaceView in `onCreate()`. Try `onStart()` - it workes for me! – Alex Cohn Sep 28 '15 at 08:45
  • Sorry for the radio silence. Actually, my service is started on the onCreate of the main activity. And then my `takePicture` method is run from the `onStartCommand` method of the service. Perhaps you can upload your project as a zip for me to take a look? Thanks – john Oct 01 '15 at 11:15
  • https://github.com/alexcohn/CameraInService – Alex Cohn Oct 01 '15 at 12:43
  • I really, really appreciate your code. But nowhere in CameraService.java do you call the `camera.takePicture` method to capture the image. The preview loads perfectly. – john Oct 01 '15 at 14:44
  • Add takePicture after preview is ready, see second commit. – Alex Cohn Oct 01 '15 at 14:53
  • I tried your code and it runs fine. To confirm the image gets captured accurately, I tried to save it to storage, and made the following modifications: https://gist.github.com/puckh/96769f8fddb53f542142#file-save-onpicturetaken-to-storage and the picture gets saved as the familiar black screen. – john Oct 01 '15 at 15:22
  • sorry, works for me ;-) See the next commit. Actually, there is no reason except exercise to convert JPEG data to Bitmap and compress it back. You can save a PNG this way, though. – Alex Cohn Oct 01 '15 at 16:50
  • Thanks for your feedback. Very helpful. I tried everything, including just running your project, but my JPG is always just a black screen. I'm using a OnePlus One (API level 21). – john Oct 01 '15 at 18:49
  • @AlexCohn tried the code on a friends phone and it worked fine (couldn't get the preview to stay invisible but the picture came out unfocused and row res but still fine). Wonder why it didn't work on my phone. Thanks for the support. Really appreciate your work. – john Oct 02 '15 at 15:23

1 Answers1

3

It happens so that the last brick in the puzzle was only revealed a week after the question was asked. The problem is specific to a particular device, namely, OnePlus One.

The sample code at https://github.com/alexcohn/CameraInService shows how this can be handled; the latest commit adds a delay between starting preview and performing takePicture(). The experiment on my wife's OnePlus One shows that delay of 100 ms is OK, but 1 ms is too short.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307