dioxus_std/geolocation/platform/
windows.rs

1use std::sync::Arc;
2
3use windows::{
4    Devices::Geolocation::{
5        BasicGeoposition, GeolocationAccessStatus, Geolocator as WindowsGeolocator,
6        PositionAccuracy, PositionChangedEventArgs, PositionStatus, StatusChangedEventArgs,
7    },
8    Foundation::TypedEventHandler,
9};
10
11use crate::geolocation::core::{Error, Event, Geocoordinates, PowerMode, Status};
12
13/// Represents the HAL's geolocator.
14pub struct Geolocator {
15    device_geolocator: WindowsGeolocator,
16}
17
18impl Geolocator {
19    /// Create a new Geolocator for the device.
20    pub fn new() -> Result<Self, Error> {
21        // Check access
22        let access_status = match WindowsGeolocator::RequestAccessAsync() {
23            Ok(v) => v,
24            Err(e) => return Err(Error::DeviceError(e.to_string())),
25        };
26
27        let access_status = match access_status.get() {
28            Ok(v) => v,
29            Err(e) => return Err(Error::DeviceError(e.to_string())),
30        };
31
32        if access_status != GeolocationAccessStatus::Allowed {
33            return Err(Error::AccessDenied);
34        }
35
36        // Get geolocator
37        let device_geolocator =
38            WindowsGeolocator::new().map_err(|e| Error::DeviceError(e.to_string()))?;
39
40        Ok(Self { device_geolocator })
41    }
42}
43
44pub async fn get_coordinates(geolocator: &Geolocator) -> Result<Geocoordinates, Error> {
45    let location = geolocator.device_geolocator.GetGeopositionAsync();
46
47    let location = match location {
48        Ok(v) => v,
49        Err(e) => return Err(Error::DeviceError(e.to_string())),
50    };
51
52    let location = match location.get() {
53        Ok(v) => v,
54        Err(e) => return Err(Error::DeviceError(e.to_string())),
55    };
56
57    let location_coordinate = match location.Coordinate() {
58        Ok(v) => v,
59        Err(e) => return Err(Error::DeviceError(e.to_string())),
60    };
61
62    let location_point = match location_coordinate.Point() {
63        Ok(v) => v,
64        Err(e) => return Err(Error::DeviceError(e.to_string())),
65    };
66
67    let position = match location_point.Position() {
68        Ok(v) => v,
69        Err(e) => return Err(Error::DeviceError(e.to_string())),
70    };
71
72    Ok(position.into())
73}
74
75/// Listen to new events with a callback.
76pub fn listen(
77    geolocator: &Geolocator,
78    callback: Arc<dyn Fn(Event) + Send + Sync>,
79) -> Result<(), Error> {
80    let callback1 = callback.clone();
81    let callback2 = callback.clone();
82
83    // Subscribe to status changed
84    geolocator
85        .device_geolocator
86        .StatusChanged(&TypedEventHandler::new(
87            move |_geolocator: &Option<WindowsGeolocator>,
88                  event_args: &Option<StatusChangedEventArgs>| {
89                if let Some(status) = event_args {
90                    // Get status
91                    let status = status.Status()?;
92
93                    // Run callback
94                    (callback1)(Event::StatusChanged(status.into()))
95                }
96                Ok(())
97            },
98        ))
99        .map_err(|e| Error::DeviceError(e.to_string()))?;
100
101    // Subscribe to position changed
102    geolocator
103        .device_geolocator
104        .PositionChanged(&TypedEventHandler::new(
105            move |_geolocator: &Option<WindowsGeolocator>,
106                  event_args: &Option<PositionChangedEventArgs>| {
107                if let Some(position) = event_args {
108                    // Get coordinate
109                    let position = position.Position()?.Coordinate()?.Point()?.Position()?;
110
111                    // Run callback
112                    (callback2)(Event::NewGeocoordinates(position.into()))
113                }
114                Ok(())
115            },
116        ))
117        .map_err(|e| Error::DeviceError(e.to_string()))?;
118
119    Ok(())
120}
121
122/// Set the device's power mode.
123pub fn set_power_mode(geolocator: &mut Geolocator, power_mode: PowerMode) -> Result<(), Error> {
124    match power_mode {
125        PowerMode::High => geolocator
126            .device_geolocator
127            .SetDesiredAccuracy(PositionAccuracy::High)
128            .map_err(|e| Error::DeviceError(e.to_string()))?,
129        PowerMode::Low => geolocator
130            .device_geolocator
131            .SetDesiredAccuracy(PositionAccuracy::Default)
132            .map_err(|e| Error::DeviceError(e.to_string()))?,
133    };
134
135    Ok(())
136}
137
138impl From<PositionStatus> for Status {
139    fn from(value: PositionStatus) -> Self {
140        match value.0 {
141            0 => Status::Ready,
142            1 => Status::Initializing,
143            3 => Status::Disabled,
144            5 => Status::NotAvailable,
145            _ => Status::Unknown,
146        }
147    }
148}
149
150impl From<BasicGeoposition> for Geocoordinates {
151    fn from(position: BasicGeoposition) -> Self {
152        Geocoordinates {
153            latitude: position.Latitude,
154            longitude: position.Longitude,
155        }
156    }
157}