Skip to main content

fission_shell_winit/
camera.rs

1use fission_core::{
2    CameraAvailability, CameraCapture, CameraCaptureRequest, CameraDevice, CameraError,
3    CameraFacing, CameraFlashlightRequest, CameraPermission, CameraPermissionRequest,
4    CANCEL_CAMERA_CAPTURE, CAPTURE_PHOTO, GET_CAMERA_AVAILABILITY, REQUEST_CAMERA_PERMISSION,
5    SET_CAMERA_FLASHLIGHT,
6};
7use fission_shell::async_host::AsyncRegistry;
8use std::sync::{Arc, Mutex};
9
10/// Host-side camera and flashlight provider.
11pub trait CameraHost: Send + Sync + 'static {
12    /// Returns camera permission state and host-visible camera devices.
13    fn availability(&self) -> Result<CameraAvailability, CameraError>;
14    /// Requests camera permission and returns the resulting permission state.
15    fn request_permission(
16        &self,
17        request: CameraPermissionRequest,
18    ) -> Result<CameraPermission, CameraError>;
19    /// Captures a still image according to the selected camera, format, flash, and quality request.
20    fn capture_photo(&self, request: CameraCaptureRequest) -> Result<CameraCapture, CameraError>;
21    /// Enables, disables, or adjusts the selected camera flashlight where available.
22    fn set_flashlight(&self, request: CameraFlashlightRequest) -> Result<(), CameraError>;
23    /// Cancels an active camera capture flow.
24    fn cancel_capture(&self) -> Result<(), CameraError>;
25}
26
27#[derive(Debug, Default)]
28pub struct UnsupportedCameraHost;
29
30impl CameraHost for UnsupportedCameraHost {
31    fn availability(&self) -> Result<CameraAvailability, CameraError> {
32        Ok(CameraAvailability {
33            permission: CameraPermission::Denied,
34            devices: Vec::new(),
35        })
36    }
37
38    fn request_permission(
39        &self,
40        _request: CameraPermissionRequest,
41    ) -> Result<CameraPermission, CameraError> {
42        Err(CameraError::unsupported("request_permission"))
43    }
44
45    fn capture_photo(&self, _request: CameraCaptureRequest) -> Result<CameraCapture, CameraError> {
46        Err(CameraError::unsupported("capture_photo"))
47    }
48
49    fn set_flashlight(&self, _request: CameraFlashlightRequest) -> Result<(), CameraError> {
50        Err(CameraError::unsupported("set_flashlight"))
51    }
52
53    fn cancel_capture(&self) -> Result<(), CameraError> {
54        Err(CameraError::unsupported("cancel_capture"))
55    }
56}
57
58#[derive(Debug)]
59pub struct MemoryCameraHost {
60    availability: CameraAvailability,
61    capture: CameraCapture,
62    flashlight_calls: Arc<Mutex<Vec<CameraFlashlightRequest>>>,
63}
64
65impl MemoryCameraHost {
66    pub fn new(availability: CameraAvailability, capture: CameraCapture) -> Self {
67        Self {
68            availability,
69            capture,
70            flashlight_calls: Arc::new(Mutex::new(Vec::new())),
71        }
72    }
73
74    pub fn flashlight_calls(&self) -> Vec<CameraFlashlightRequest> {
75        self.flashlight_calls
76            .lock()
77            .map(|calls| calls.clone())
78            .unwrap_or_default()
79    }
80}
81
82impl Default for MemoryCameraHost {
83    fn default() -> Self {
84        Self::new(
85            CameraAvailability {
86                permission: CameraPermission::Granted,
87                devices: vec![CameraDevice {
88                    id: "memory-camera".into(),
89                    label: Some("Memory camera".into()),
90                    facing: CameraFacing::Back,
91                    has_flashlight: true,
92                }],
93            },
94            CameraCapture {
95                bytes: vec![0xff, 0xd8, 0xff, 0xd9],
96                content_type: "image/jpeg".into(),
97                width: 1,
98                height: 1,
99                camera_id: Some("memory-camera".into()),
100            },
101        )
102    }
103}
104
105impl CameraHost for MemoryCameraHost {
106    fn availability(&self) -> Result<CameraAvailability, CameraError> {
107        Ok(self.availability.clone())
108    }
109
110    fn request_permission(
111        &self,
112        _request: CameraPermissionRequest,
113    ) -> Result<CameraPermission, CameraError> {
114        Ok(self.availability.permission)
115    }
116
117    fn capture_photo(&self, _request: CameraCaptureRequest) -> Result<CameraCapture, CameraError> {
118        Ok(self.capture.clone())
119    }
120
121    fn set_flashlight(&self, request: CameraFlashlightRequest) -> Result<(), CameraError> {
122        self.flashlight_calls.lock().unwrap().push(request);
123        Ok(())
124    }
125
126    fn cancel_capture(&self) -> Result<(), CameraError> {
127        Ok(())
128    }
129}
130
131pub(crate) fn register_camera_capabilities(
132    async_registry: &mut AsyncRegistry,
133    host: Arc<dyn CameraHost>,
134) {
135    let availability_host = host.clone();
136    async_registry.register_operation_capability(GET_CAMERA_AVAILABILITY, move |(), _| {
137        let host = availability_host.clone();
138        async move { host.availability() }
139    });
140
141    let permission_host = host.clone();
142    async_registry.register_operation_capability(REQUEST_CAMERA_PERMISSION, move |request, _| {
143        let host = permission_host.clone();
144        async move { host.request_permission(request) }
145    });
146
147    let capture_host = host.clone();
148    async_registry.register_operation_capability(CAPTURE_PHOTO, move |request, _| {
149        let host = capture_host.clone();
150        async move { host.capture_photo(request) }
151    });
152
153    let flashlight_host = host.clone();
154    async_registry.register_operation_capability(SET_CAMERA_FLASHLIGHT, move |request, _| {
155        let host = flashlight_host.clone();
156        async move { host.set_flashlight(request) }
157    });
158
159    async_registry.register_operation_capability(CANCEL_CAMERA_CAPTURE, move |(), _| {
160        let host = host.clone();
161        async move { host.cancel_capture() }
162    });
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use fission_core::CameraImageFormat;
169
170    #[test]
171    fn unsupported_host_reports_errors() {
172        let host = UnsupportedCameraHost;
173        assert!(host
174            .capture_photo(CameraCaptureRequest {
175                format: CameraImageFormat::Jpeg,
176                ..Default::default()
177            })
178            .is_err());
179        assert!(host
180            .set_flashlight(CameraFlashlightRequest {
181                enabled: true,
182                ..Default::default()
183            })
184            .is_err());
185    }
186
187    #[test]
188    fn memory_host_returns_capture_and_records_flashlight() {
189        let host = MemoryCameraHost::default();
190        let availability = host.availability().unwrap();
191        assert_eq!(availability.permission, CameraPermission::Granted);
192
193        let capture = host.capture_photo(CameraCaptureRequest::default()).unwrap();
194        assert_eq!(capture.content_type, "image/jpeg");
195
196        let request = CameraFlashlightRequest {
197            enabled: true,
198            intensity: Some(128),
199            ..Default::default()
200        };
201        host.set_flashlight(request.clone()).unwrap();
202        assert_eq!(host.flashlight_calls(), vec![request]);
203    }
204}