1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.camera.lifecycle
17 
18 import android.content.Context
19 import android.content.pm.PackageManager
20 import androidx.annotation.RestrictTo
21 import androidx.annotation.RestrictTo.Scope
22 import androidx.camera.core.Camera
23 import androidx.camera.core.CameraInfo
24 import androidx.camera.core.CameraProvider
25 import androidx.camera.core.CameraSelector
26 import androidx.camera.core.CameraXConfig
27 import androidx.camera.core.CompositionSettings
28 import androidx.camera.core.ConcurrentCamera
29 import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
30 import androidx.camera.core.ImageAnalysis
31 import androidx.camera.core.ImageCapture
32 import androidx.camera.core.Preview
33 import androidx.camera.core.UseCase
34 import androidx.camera.core.UseCaseGroup
35 import androidx.camera.core.impl.utils.executor.CameraXExecutors
36 import androidx.camera.core.impl.utils.futures.Futures
37 import androidx.concurrent.futures.await
38 import androidx.core.util.Preconditions
39 import androidx.lifecycle.Lifecycle
40 import androidx.lifecycle.LifecycleOwner
41 import com.google.common.util.concurrent.ListenableFuture
42 
43 /**
44  * Provides access to a camera which has its opening and closing controlled by a [LifecycleOwner].
45  *
46  * This allows configuring multiple instances with different [Context] and [CameraXConfig]. The use
47  * cases can be bound to different camera providers simultaneously, but only one [LifecycleOwner]
48  * can be [active][Lifecycle.State.RESUMED] at a time.
49  *
50  * The sample shows how to configure and create two camera providers differently.
51  *
52  * @sample androidx.camera.lifecycle.samples.configureAndCreateInstances
53  */
54 // TODO: Remove the annotation when LifecycleCameraProvider is ready to be public.
55 @RestrictTo(Scope.LIBRARY_GROUP)
56 public interface LifecycleCameraProvider : CameraProvider {
57     /**
58      * Returns `true` if the [UseCase] is bound to a lifecycle. Otherwise returns `false`.
59      *
60      * After binding a use case, use cases remain bound until the lifecycle reaches a
61      * [Lifecycle.State.DESTROYED] state or if is unbound by calls to [unbind] or [unbindAll].
62      */
isBoundnull63     public fun isBound(useCase: UseCase): Boolean
64 
65     /**
66      * Unbinds all specified use cases from the lifecycle provider.
67      *
68      * This will initiate a close of every open camera which has zero [UseCase] associated with it
69      * at the end of this call.
70      *
71      * If a use case in the argument list is not bound, then it is simply ignored.
72      *
73      * After unbinding a UseCase, the UseCase can be bound to another [Lifecycle] however listeners
74      * and settings should be reset by the application.
75      *
76      * @param useCases The collection of use cases to remove.
77      * @throws IllegalStateException If not called on main thread.
78      * @throws UnsupportedOperationException If called in concurrent mode.
79      */
80     public fun unbind(vararg useCases: UseCase?): Unit
81 
82     /**
83      * Unbinds all use cases from the lifecycle provider and removes them from CameraX.
84      *
85      * This will initiate a close of every currently open camera.
86      *
87      * @throws IllegalStateException If not called on main thread.
88      */
89     public fun unbindAll(): Unit
90 
91     /**
92      * Binds the collection of [UseCase] to a [LifecycleOwner].
93      *
94      * The state of the lifecycle will determine when the cameras are open, started, stopped and
95      * closed. When started, the use cases receive camera data.
96      *
97      * Binding to a lifecycleOwner in state currently in [Lifecycle.State.STARTED] or greater will
98      * also initialize and start data capture. If the camera was already running this may cause a
99      * new initialization to occur temporarily stopping data from the camera before restarting it.
100      *
101      * Multiple use cases can be bound via adding them all to a single bindToLifecycle call, or by
102      * using multiple bindToLifecycle calls. Using a single call that includes all the use cases
103      * helps to set up a camera session correctly for all uses cases, such as by allowing
104      * determination of resolutions depending on all the use cases bound being bound. If the use
105      * cases are bound separately, it will find the supported resolution with the priority depending
106      * on the binding sequence. If the use cases are bound with a single call, it will find the
107      * supported resolution with the priority in sequence of [ImageCapture], [Preview] and then
108      * [ImageAnalysis]. The resolutions that can be supported depends on the camera device hardware
109      * level that there are some default guaranteed resolutions listed in
110      * [android.hardware.camera2.CameraDevice.createCaptureSession].
111      *
112      * Currently up to 3 use cases may be bound to a [Lifecycle] at any time. Exceeding capability
113      * of target camera device will throw an IllegalArgumentException.
114      *
115      * A UseCase should only be bound to a single lifecycle and camera selector a time. Attempting
116      * to bind a use case to a lifecycle when it is already bound to another lifecycle is an error,
117      * and the use case binding will not change. Attempting to bind the same use case to multiple
118      * camera selectors is also an error and will not change the binding.
119      *
120      * If different use cases are bound to different camera selectors that resolve to distinct
121      * cameras, but the same lifecycle, only one of the cameras will operate at a time. The
122      * non-operating camera will not become active until it is the only camera with use cases bound.
123      *
124      * The [Camera] returned is determined by the given camera selector, plus other internal
125      * requirements, possibly from use case configurations. The camera returned from bindToLifecycle
126      * may differ from the camera determined solely by a camera selector. If the camera selector
127      * can't resolve a valid camera under the requirements, an IllegalArgumentException will be
128      * thrown.
129      *
130      * Only [UseCase] bound to latest active [Lifecycle] can keep alive. [UseCase] bound to other
131      * [Lifecycle] will be stopped.
132      *
133      * @param lifecycleOwner The lifecycleOwner which controls the lifecycle transitions of the use
134      *   cases.
135      * @param cameraSelector The camera selector which determines the camera to use for set of use
136      *   cases.
137      * @param useCases The use cases to bind to a lifecycle.
138      * @return The [Camera] instance which is determined by the camera selector and internal
139      *   requirements.
140      * @throws IllegalStateException If the use case has already been bound to another lifecycle or
141      *   method is not called on main thread.
142      * @throws IllegalArgumentException If the provided camera selector is unable to resolve a
143      *   camera to be used for the given use cases.
144      * @throws UnsupportedOperationException If the camera is configured in concurrent mode.
145      */
146     public fun bindToLifecycle(
147         lifecycleOwner: LifecycleOwner,
148         cameraSelector: CameraSelector,
149         vararg useCases: UseCase?
150     ): Camera
151 
152     /**
153      * Binds a [UseCaseGroup] to a [LifecycleOwner].
154      *
155      * Similar to [bindToLifecycle], with the addition that the bound collection of [UseCase] share
156      * parameters defined by [UseCaseGroup] such as consistent camera sensor rect across all
157      * [UseCase]s.
158      *
159      * If one [UseCase] is in multiple [UseCaseGroup]s, it will be linked to the [UseCaseGroup] in
160      * the latest [bindToLifecycle] call.
161      *
162      * @throws UnsupportedOperationException If the camera is configured in concurrent mode.
163      */
164     public fun bindToLifecycle(
165         lifecycleOwner: LifecycleOwner,
166         cameraSelector: CameraSelector,
167         useCaseGroup: UseCaseGroup
168     ): Camera
169 
170     /**
171      * Binds list of [SingleCameraConfig]s to [LifecycleOwner].
172      *
173      * The concurrent camera is only supporting two cameras currently. If the input list of
174      * [SingleCameraConfig]s have less or more than two [SingleCameraConfig]s,
175      * [IllegalArgumentException] will be thrown. If cameras are already used by other [UseCase]s,
176      * [UnsupportedOperationException] will be thrown.
177      *
178      * A logical camera is a grouping of two or more of those physical cameras. See
179      * [Multi-camera API](https://developer.android.com/media/camera/camera2/multi-camera)
180      *
181      * If we want to open concurrent logical cameras, which are one front camera and one back
182      * camera, the device needs to support [PackageManager.FEATURE_CAMERA_CONCURRENT]. To set up
183      * concurrent logical camera, call [availableConcurrentCameraInfos] to get the list of available
184      * combinations of concurrent cameras. Each sub-list contains the [CameraInfo]s for a
185      * combination of cameras that can be operated concurrently. Each logical camera can have its
186      * own [UseCase]s and [LifecycleOwner]. See
187      * [CameraX lifecycles]({@docRoot}training/camerax/architecture#lifecycles)
188      *
189      * If the concurrent logical cameras are binding the same preview and video capture use cases,
190      * the concurrent cameras video recording will be supported. The concurrent camera preview
191      * stream will be shared with video capture and record the concurrent cameras streams as a
192      * composited stream. The [CompositionSettings] can be used to configure the position of each
193      * camera stream and different layouts can be built. See [CompositionSettings] for more details.
194      *
195      * If we want to open concurrent physical cameras, which are two front cameras or two back
196      * cameras, the device needs to support physical cameras and the capability could be checked via
197      * [CameraInfo.isLogicalMultiCameraSupported]. Each physical cameras can have its own [UseCase]s
198      * but needs to have the same [LifecycleOwner], otherwise [IllegalArgumentException] will be
199      * thrown.
200      *
201      * If we want to open one physical camera, for example ultra wide, we just need to set physical
202      * camera id in [CameraSelector] and bind to lifecycle. All CameraX features will work normally
203      * when only a single physical camera is used.
204      *
205      * If we want to open multiple physical cameras, we need to have multiple [CameraSelector]s,
206      * each in one [SingleCameraConfig] and set physical camera id, then bind to lifecycle with the
207      * [SingleCameraConfig]s. Internally each physical camera id will be set on [UseCase], for
208      * example, [Preview] and call
209      * [android.hardware.camera2.params.OutputConfiguration.setPhysicalCameraId].
210      *
211      * Currently only two physical cameras for the same logical camera id are allowed and the device
212      * needs to support physical cameras by checking [CameraInfo.isLogicalMultiCameraSupported]. In
213      * addition, there is no guarantee or API to query whether the device supports multiple physical
214      * camera opening or not. Internally the library checks
215      * [android.hardware.camera2.CameraDevice.isSessionConfigurationSupported], if the device does
216      * not support the multiple physical camera configuration, [IllegalArgumentException] will be
217      * thrown.
218      *
219      * @param singleCameraConfigs Input list of [SingleCameraConfig]s.
220      * @return Output [ConcurrentCamera] instance.
221      * @throws IllegalArgumentException If less or more than two camera configs are provided.
222      * @throws UnsupportedOperationException If device is not supporting concurrent camera or
223      *   cameras are already used by other [UseCase]s.
224      * @see ConcurrentCamera
225      * @see availableConcurrentCameraInfos
226      * @see CameraInfo.isLogicalMultiCameraSupported
227      * @see CameraInfo.getPhysicalCameraInfos
228      */
229     public fun bindToLifecycle(singleCameraConfigs: List<SingleCameraConfig?>): ConcurrentCamera
230 
231     public companion object {
232         /**
233          * Creates a lifecycle camera provider instance.
234          *
235          * @param context The Application context.
236          * @param cameraXConfig The configuration options to configure the lifecycle camera
237          *   provider. If not set, the default configuration will be used.
238          * @return The lifecycle camera provider instance.
239          */
240         @JvmOverloads
241         @JvmStatic
242         public suspend fun createInstance(
243             context: Context,
244             cameraXConfig: CameraXConfig? = null,
245         ): LifecycleCameraProvider {
246             return createInstanceAsync(context, cameraXConfig).await()
247         }
248 
249         /**
250          * Creates a lifecycle camera provider instance asynchronously.
251          *
252          * @param context The Application context.
253          * @param cameraXConfig The configuration options to configure the lifecycle camera
254          *   provider. If not set, the default configuration will be used.
255          * @return A [ListenableFuture] that will be completed when the lifecycle camera provider
256          *   instance is initialized.
257          */
258         @JvmOverloads
259         @JvmStatic
260         public fun createInstanceAsync(
261             context: Context,
262             cameraXConfig: CameraXConfig? = null,
263         ): ListenableFuture<LifecycleCameraProvider> {
264             Preconditions.checkNotNull(context)
265             val lifecycleCameraProvider = LifecycleCameraProviderImpl()
266             return Futures.transform(
267                 lifecycleCameraProvider.initAsync(context, cameraXConfig),
268                 { lifecycleCameraProvider },
269                 CameraXExecutors.directExecutor()
270             )
271         }
272     }
273 }
274