0

I want the camera to freeze preview when the image capture button is pressed. I have looked at other stack-overflow questions but they they are outdated.

cameraX version I am using: 1.0.0-beta03

Any help will be appreciated. Thanks

Amrit
  • 87
  • 1
  • 4
  • 8

2 Answers2

5

There isn't a "proper" way to do it yet, but a couple of ways to achieve it include:

  • Stop the preview stream by unbinding the Preview use case, take the picture, then bind a new Preview use case to continue the preview stream. For this approach to work, you'd need to also have an ImageAnalysis use case bound, since ImageCapture cannot take a picture if it's the only bound use case (another Preview or ImageAnalysis use case has to also be bound, check out the official documentation on the possible use case combinations). While this approach works, it would result in a slight delay between the moment the ImageCapture callback is called, and when the preview continues, during this time the screen will be blank.

  • At the moment you take a picture, use the latest frame from ImageAnalysis to display it on top of the preview, you can convert the frame -which is an ImageProxy- to a Bitmap and display it in an ImageView. Once the ImageCapture callback is invoked, remove the frame and continue the preview.

Edit:

Starting from camera-view version 1.0.0-alpha12, you can get a Bitmap representation of the preview using PreviewView.getBitmap(). Using this method, you can get the Bitmap representation of the preview at the moment the user takes a picture, show it on top of the preview in an ImageView, then once an image capture result is available, hide the image.

Husayn Hakeem
  • 4,184
  • 1
  • 16
  • 31
0

Also I think you can try and create (as a workaround) a custom LifecycleOwner wrapper and manually set lifecycle state to ON_PAUSE when you want to pause preview. So it will be something like this:

class CameraXLifecycleOwner : LifecycleOwner {

    private val lifecycle = LifecycleRegistry(this)

    private val observer = object : LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        fun onCreate() {
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        fun onResume() {
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onStart() {
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        fun onPause() {
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onStop() {
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun onDestroy() {
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        }
}

fun attachLifecycleOwner(lifecycleOwner: LifecycleOwner) {
    lifecycle.currentState = lifecycleOwner.lifecycle.currentState
    lifecycleOwner.lifecycle.addObserver(observer)
}

fun detachLifecycleOwner(lifecycleOwner: LifecycleOwner) {
    lifecycleOwner.lifecycle.removeObserver(observer)
}

fun pauseCamera() {
    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}

fun resumeCamera() {
    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}

override fun getLifecycle(): Lifecycle = lifecycle

}

So you then wrap default fragment's viewLifecycleOwner by passing it to attachLifecycleOwner() and this way the wrapper will repeat all viewLifecycleOwner's states, but then you can also call pauseCamera()/resumeCamera() to manually change state faking onPause/onResume state change. Not sure it's the best idea, but it's an idea.

Oleksii Urusov
  • 378
  • 2
  • 8