1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! Geolocation
use async_trait::async_trait;
use fantoccini::error::CmdError;
use http::Method;
use serde_derive::{Deserialize, Serialize};
use serde_json::json;
use crate::{AndroidClient, AppiumClientTrait, IOSClient};
use crate::commands::AppiumCommand;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Location {
    pub latitude: f64,
    pub longitude: f64,
    pub altitude: f64,
}

impl Location {
    pub fn new(latitude: f64, longitude: f64, altitude: f64) -> Location {
        Location {
            latitude, longitude, altitude
        }
    }
}

/// Retrieve current geolocation (or set it on emulator)
#[async_trait]
pub trait SupportsLocation : AppiumClientTrait {
    async fn location(&self) -> Result<Location, CmdError> {
        let value = self.issue_cmd(AppiumCommand::Custom(
            Method::GET,
            "location".to_string(),
            None
        )).await?;

        Ok(serde_json::from_value(value)?)
    }

    /// Tries to set location if the driver/device supports it. Returns location of device after the attempt.
    ///
    /// Due to configuration or limitation of device, the location change may fail silently.
    /// The returned [Location] is the location that the device currently uses (or where is actually is).
    async fn set_location(&self, location: Location) -> Result<Location, CmdError> {
        let value = self.issue_cmd(AppiumCommand::Custom(
            Method::POST,
            "location".to_string(),
            Some(json!({
                "location": location
            }))
        )).await?;

        Ok(serde_json::from_value(value)?)
    }
}

#[async_trait]
impl SupportsLocation for AndroidClient {}

#[async_trait]
impl SupportsLocation for IOSClient {}

#[derive(Clone, Debug, Serialize)]
pub struct AndroidGeoLocation {
    pub latitude: f64,
    pub longitude: f64,
    pub altitude: f64,
    pub satellites: u32,
    pub speed: f64,
}

impl AndroidGeoLocation {
    pub fn new(location: Location, satellites: u32, speed: f64) -> AndroidGeoLocation {
        AndroidGeoLocation {
            latitude: location.latitude,
            longitude: location.longitude,
            altitude: location.altitude,
            satellites,
            speed
        }
    }
}

/// Set android geolocation (with extended options)
#[async_trait]
pub trait SupportsAndroidLocation : AppiumClientTrait {
    async fn set_android_location(&self, location: AndroidGeoLocation) -> Result<Location, CmdError> {
        let value = self.issue_cmd(AppiumCommand::Custom(
            Method::POST,
            "location".to_string(),
            Some(json!({
                "location": location
            }))
        )).await?;

        Ok(serde_json::from_value(value)?)
    }

}

#[async_trait]
impl SupportsAndroidLocation for AndroidClient {}