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
10pub trait CameraHost: Send + Sync + 'static {
12 fn availability(&self) -> Result<CameraAvailability, CameraError>;
14 fn request_permission(
16 &self,
17 request: CameraPermissionRequest,
18 ) -> Result<CameraPermission, CameraError>;
19 fn capture_photo(&self, request: CameraCaptureRequest) -> Result<CameraCapture, CameraError>;
21 fn set_flashlight(&self, request: CameraFlashlightRequest) -> Result<(), CameraError>;
23 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}