1 /*
2  * Copyright (C) 2024 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 
17 package com.android.deviceaswebcam
18 
19 import android.content.Context
20 import android.graphics.SurfaceTexture
21 import android.util.Size
22 import com.android.deviceaswebcam.CameraController.RotationUpdateListener
23 import java.util.function.Consumer
24 
25 class WebcamControllerImpl(context: Context) : WebcamController() {
26     private val mLock = Object()
27 
28     private val mCameraController = CameraController(context, /* webcamController= */ this)
29     private var mDestroyActivityCallback: Runnable? = null
30 
setStreamConfignull31     override fun setStreamConfig(size: Size, frameRate: Int) {
32         synchronized(mLock) {
33             mCameraController.setWebcamStreamConfig(size.width, size.height, frameRate)
34         }
35     }
36 
startStreamnull37     override fun startStream() {
38         synchronized(mLock) { mCameraController.startWebcamStreaming() }
39     }
40 
stopStreamnull41     override fun stopStream() {
42         synchronized(mLock) { mCameraController.stopWebcamStreaming() }
43     }
44 
onImageReturnednull45     override fun onImageReturned(token: Long) {
46         synchronized(mLock) { mCameraController.returnImage(token) }
47     }
48 
onDestroynull49     override fun onDestroy() {
50         synchronized(mLock) { mDestroyActivityCallback?.run() }
51     }
52 
53     /**
54      * Method to set a preview surface texture that camera will stream to. Should be of the size
55      * returned by [.getSuitablePreviewSize].
56      *
57      * @param surfaceTexture surfaceTexture to stream preview frames to
58      * @param previewSize the preview size
59      * @param previewSizeChangeListener a listener to monitor the preview size change events.
60      */
setPreviewSurfaceTexturenull61     fun setPreviewSurfaceTexture(
62         surfaceTexture: SurfaceTexture,
63         previewSize: Size,
64         previewSizeChangeListener: Consumer<Size>?
65     ) {
66         synchronized(mLock) {
67             mCameraController.startPreviewStreaming(
68                 surfaceTexture,
69                 previewSize,
70                 previewSizeChangeListener
71             )
72         }
73     }
74 
75     /** Returns the available [CameraId] list. */
getAvailableCameraIdsnull76     fun getAvailableCameraIds(): List<CameraId> {
77         synchronized(mLock) {
78             return mCameraController.availableCameraIds
79         }
80     }
81 
82     /** Returns current rotation degrees value. */
getCurrentRotationnull83     fun getCurrentRotation(): Int {
84         synchronized(mLock) {
85             return mCameraController.currentRotation
86         }
87     }
88 
89     /** Sets a [CameraController.RotationUpdateListener] to monitor the device rotation changes. */
setRotationUpdateListenernull90     fun setRotationUpdateListener(listener: RotationUpdateListener?) {
91         synchronized(mLock) { mCameraController.setRotationUpdateListener(listener) }
92     }
93 
94     /** Method to remove any preview SurfaceTexture set by [.setPreviewSurfaceTexture]. */
removePreviewSurfaceTexturenull95     fun removePreviewSurfaceTexture() {
96         synchronized(mLock) { mCameraController.stopPreviewStreaming() }
97     }
98 
99     /** Sets the new zoom ratio setting to the working camera. */
setZoomRationull100     fun setZoomRatio(zoomRatio: Float) {
101         synchronized(mLock) { mCameraController.zoomRatio = zoomRatio }
102     }
103 
104     /** Returns the [CameraInfo] of the working camera. */
getCameraInfonull105     fun getCameraInfo(): CameraInfo? {
106         synchronized(mLock) {
107             return mCameraController.cameraInfo
108         }
109     }
110 
111     /**
112      * Retrieves current tap-to-focus points.
113      *
114      * @return the normalized points or `null` if it is auto-focus mode currently.
115      */
getTapToFocusPointsnull116     fun getTapToFocusPoints(): FloatArray? {
117         synchronized(mLock) {
118             return mCameraController.tapToFocusPoints
119         }
120     }
121 
122     /** Returns true if high quality mode is enabled, false otherwise */
isHighQualityModeEnablednull123     fun isHighQualityModeEnabled(): Boolean {
124         synchronized(mLock) {
125             return mCameraController.isHighQualityModeEnabled
126         }
127     }
128 
129     /**
130      * Enables/Disables high quality mode. See [CameraController.setHighQualityModeEnabled] for more
131      * info.
132      */
setHighQualityModeEnablednull133     fun setHighQualityModeEnabled(enabled: Boolean, callback: Runnable) {
134         synchronized(mLock) { mCameraController.setHighQualityModeEnabled(enabled, callback) }
135     }
136 
137     /**
138      * Returns the best suitable output size for preview.
139      *
140      * If the webcam stream doesn't exist, find the largest 16:9 supported output size which is not
141      * larger than 1080p. If the webcam stream exists, find the largest supported output size which
142      * matches the aspect ratio of the webcam stream size and is not larger than the webcam stream
143      * size.
144      */
getSuitablePreviewSizenull145     fun getSuitablePreviewSize(): Size? {
146         synchronized(mLock) {
147             return mCameraController.suitablePreviewSize
148         }
149     }
150 
151     /** Returns current zoom ratio setting. */
getZoomRationull152     fun getZoomRatio(): Float {
153         synchronized(mLock) {
154             return mCameraController.zoomRatio
155         }
156     }
157 
158     /**
159      * Method to setOnDestroyedCallback. This callback will be called when immediately before the
160      * foreground service is destroyed. Intended to give and bound context a change to clean up
161      * before the Service is destroyed. `setOnDestroyedCallback(null)` must be called to unset the
162      * callback when a bound context finishes to prevent Context leak.
163      *
164      * This callback must not call `setOnDestroyedCallback` from within the callback.
165      *
166      * @param callback callback to be called when the service is destroyed. `null` unsets the
167      *   callback
168      */
setOnDestroyedCallbacknull169     fun setOnDestroyedCallback(callback: Runnable?) {
170         synchronized(mLock) { mDestroyActivityCallback = callback }
171     }
172 
173     /** Returns the [CameraInfo] for the specified camera id. */
getOrCreateCameraInfonull174     fun getOrCreateCameraInfo(cameraId: CameraId): CameraInfo? {
175         synchronized(mLock) {
176             return mCameraController.getOrCreateCameraInfo(cameraId)
177         }
178     }
179 
180     /** Toggles camera between the back and front cameras. */
toggleCameranull181     fun toggleCamera() {
182         synchronized(mLock) { mCameraController.toggleCamera() }
183     }
184 
185     /** Switches current working camera to specific one. */
switchCameranull186     fun switchCamera(cameraId: CameraId) {
187         synchronized(mLock) { mCameraController.switchCamera(cameraId) }
188     }
189 
190     /** Resets to the auto-focus mode. */
resetToAutoFocusnull191     fun resetToAutoFocus() {
192         synchronized(mLock) { mCameraController.resetToAutoFocus() }
193     }
194 
195     /**
196      * Trigger tap-to-focus operation for the specified normalized points mapping to the FOV.
197      *
198      * The specified normalized points will be used to calculate the corresponding metering
199      * rectangles that will be applied for AF, AE and AWB.
200      */
tapToFocusnull201     fun tapToFocus(normalizedPoint: FloatArray?) {
202         synchronized(mLock) { mCameraController.tapToFocus(normalizedPoint) }
203     }
204 
205     companion object {
206         private const val TAG = "WebcamControllerImpl"
207     }
208 }
209