1use async_trait::async_trait;
12use smix_simctl::{SimctlClient, SimctlError, SimctlPermission};
13use std::path::Path;
14
15pub use crate::PermissionAction;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub enum Permission {
27 Camera,
28 Microphone,
29 PhotoLibrary,
30 Location,
31 LocationAlways,
32 Notifications,
33 Contacts,
34 Calendar,
35 Reminders,
36 Bluetooth,
37 Motion,
38 Media,
39 Health,
40 FaceId,
42 HomeKit,
44 Storage,
47 PostNotifications,
50}
51
52impl Permission {
53 #[must_use]
56 pub fn to_simctl(self) -> Option<SimctlPermission> {
57 match self {
58 Permission::Camera => Some(SimctlPermission::Camera),
59 Permission::Microphone => Some(SimctlPermission::Microphone),
60 Permission::PhotoLibrary => Some(SimctlPermission::Photos),
61 Permission::Location => Some(SimctlPermission::Location),
62 Permission::LocationAlways => Some(SimctlPermission::LocationAlways),
63 Permission::Notifications | Permission::PostNotifications => {
64 Some(SimctlPermission::Notifications)
65 }
66 Permission::Contacts => Some(SimctlPermission::Contacts),
67 Permission::Calendar => Some(SimctlPermission::Calendar),
68 Permission::Reminders => Some(SimctlPermission::Reminders),
69 Permission::Bluetooth => Some(SimctlPermission::Bluetooth),
70 Permission::Motion => Some(SimctlPermission::Motion),
71 Permission::Media => Some(SimctlPermission::Media),
72 Permission::Health => Some(SimctlPermission::Health),
73 Permission::FaceId => Some(SimctlPermission::Faceid),
74 Permission::HomeKit => Some(SimctlPermission::HomeKit),
75 Permission::Storage => None,
76 }
77 }
78
79 #[must_use]
82 pub fn from_simctl(perm: SimctlPermission) -> Self {
83 match perm {
84 SimctlPermission::Camera => Permission::Camera,
85 SimctlPermission::Microphone => Permission::Microphone,
86 SimctlPermission::Photos => Permission::PhotoLibrary,
87 SimctlPermission::Location => Permission::Location,
88 SimctlPermission::LocationAlways => Permission::LocationAlways,
89 SimctlPermission::Notifications => Permission::Notifications,
90 SimctlPermission::Contacts => Permission::Contacts,
91 SimctlPermission::Calendar => Permission::Calendar,
92 SimctlPermission::Reminders => Permission::Reminders,
93 SimctlPermission::Bluetooth => Permission::Bluetooth,
94 SimctlPermission::Motion => Permission::Motion,
95 SimctlPermission::Media => Permission::Media,
96 SimctlPermission::Health => Permission::Health,
97 SimctlPermission::Faceid => Permission::FaceId,
98 SimctlPermission::HomeKit => Permission::HomeKit,
99 SimctlPermission::AddressBook => Permission::Contacts,
100 }
101 }
102
103 #[must_use]
107 pub fn to_android(self) -> Option<&'static str> {
108 match self {
109 Permission::Camera => Some("android.permission.CAMERA"),
110 Permission::Microphone => Some("android.permission.RECORD_AUDIO"),
111 Permission::PhotoLibrary => Some("android.permission.READ_MEDIA_IMAGES"),
112 Permission::Location => Some("android.permission.ACCESS_FINE_LOCATION"),
113 Permission::LocationAlways => Some("android.permission.ACCESS_BACKGROUND_LOCATION"),
114 Permission::Notifications | Permission::PostNotifications => {
115 Some("android.permission.POST_NOTIFICATIONS")
116 }
117 Permission::Contacts => Some("android.permission.READ_CONTACTS"),
118 Permission::Calendar => Some("android.permission.READ_CALENDAR"),
119 Permission::Bluetooth => Some("android.permission.BLUETOOTH_CONNECT"),
120 Permission::Motion => Some("android.permission.ACTIVITY_RECOGNITION"),
121 Permission::Media => Some("android.permission.READ_MEDIA_AUDIO"),
122 Permission::Storage => Some("android.permission.WRITE_EXTERNAL_STORAGE"),
123 Permission::Reminders
124 | Permission::Health
125 | Permission::FaceId
126 | Permission::HomeKit => None,
127 }
128 }
129}
130
131#[async_trait]
139pub trait DeviceControl: Send + Sync {
140 fn platform(&self) -> smix_driver::Platform;
142
143 fn as_ios_simctl(&self) -> Option<&SimctlClient> {
146 None
147 }
148
149 async fn launch(&self, udid: &str, bundle_id: &str) -> Result<u32, SimctlError>;
152 async fn launch_with_args(
153 &self,
154 udid: &str,
155 bundle_id: &str,
156 args: &[String],
157 ) -> Result<u32, SimctlError>;
158 async fn terminate(&self, udid: &str, bundle_id: &str) -> Result<(), SimctlError>;
159 async fn install(&self, udid: &str, app_path: &str) -> Result<(), SimctlError>;
160 async fn uninstall(&self, udid: &str, bundle_id: &str) -> Result<(), SimctlError>;
161 async fn keychain_reset(&self, udid: &str) -> Result<(), SimctlError>;
162
163 async fn privacy_reset_all(&self, _udid: &str, _bundle_id: &str) -> Result<(), SimctlError> {
171 Ok(())
172 }
173
174 async fn clear_app_sandbox(&self, _udid: &str, _bundle_id: &str) -> Result<(), SimctlError> {
179 Ok(())
180 }
181
182 async fn user_defaults_delete(
195 &self,
196 _udid: &str,
197 _bundle_id: &str,
198 _key: &str,
199 ) -> Result<bool, SimctlError> {
200 Err(SimctlError::non_zero_exit(
201 "user-defaults-delete",
202 1,
203 "clearUserDefaults is not supported on this platform (iOS simulator only — \
204 Android SharedPreferences has no host-side per-key deletion; use clearAppData \
205 for a full store wipe)",
206 ))
207 }
208
209 async fn open_url(&self, udid: &str, url: &str) -> Result<(), SimctlError>;
212 async fn send_push(
213 &self,
214 udid: &str,
215 bundle_id: &str,
216 apns_json_path: &str,
217 ) -> Result<(), SimctlError>;
218 async fn screenshot(&self, udid: &str) -> Result<Vec<u8>, SimctlError>;
219
220 async fn pasteboard_set(&self, udid: &str, text: &str) -> Result<(), SimctlError>;
223 async fn pasteboard_get(&self, udid: &str) -> Result<String, SimctlError>;
224 async fn add_media(&self, udid: &str, paths: &[String]) -> Result<(), SimctlError>;
225 async fn location_set(&self, udid: &str, lat: f64, lon: f64) -> Result<(), SimctlError>;
226 async fn location_start(
227 &self,
228 udid: &str,
229 points: &[(f64, f64)],
230 speed_mps: Option<f64>,
231 ) -> Result<(), SimctlError>;
232
233 async fn set_permission(
236 &self,
237 udid: &str,
238 bundle_id: &str,
239 permission: Permission,
240 action: PermissionAction,
241 ) -> Result<(), SimctlError>;
242
243 async fn start_recording(&self, udid: &str, output_path: &Path) -> Result<(), SimctlError>;
246 async fn stop_recording(&self) -> Result<(), SimctlError>;
247}